handbrake 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,19 @@
1
+ 0.3.0
2
+ =====
3
+
4
+ - Change the output from the `scan` action to be a {HandBrake::Disc}
5
+ object which contains a titles hash, rather than the hash directly.
6
+ - When `scan`ning for a single title, return only a single
7
+ {HandBrake::Title}.
8
+ - Accept a path for the `:atomic` option to
9
+ {HandBrake::CLI#output}. If specified, the temporary file will be
10
+ written to this path rather than the ultimate target directory.
11
+ - Ensure that trace mode prints updating lines (e.g., the encode
12
+ status) that do not end in newlines in a timely fashion.
13
+ - Add property-based constructors to {HandBrake::Title} and
14
+ {HandBrake::Chapter} for easier construction in tests of consuming
15
+ apps/libs.
16
+
1
17
  0.2.1
2
18
  =====
3
19
 
data/README.md CHANGED
@@ -39,13 +39,13 @@ A brief sample:
39
39
 
40
40
  project = hb.input('/Volumes/Arcturan Megafreighter/DVDs/L')
41
41
 
42
- titles = project.scan
43
- titles[1].number # => 1
44
- titles[1].main_feature? # => true
45
- titles[1].duration # => "01:21:18"
46
- titles[1].seconds # => 4878
47
- titles[1].chapters.size # => 23
48
- titles[1].chapters[3].seconds # => 208
42
+ disc = project.scan
43
+ disc.titles[1].number # => 1
44
+ disc.titles[1].main_feature? # => true
45
+ disc.titles[1].duration # => "01:21:18"
46
+ disc.titles[1].seconds # => 4878
47
+ disc.titles[1].chapters.size # => 23
48
+ disc.titles[1].chapters[3].seconds # => 208
49
49
 
50
50
  project.title(1).
51
51
  preset('Normal').
@@ -99,10 +99,9 @@ come in any order, a few must come last and have particular return
99
99
  values:
100
100
 
101
101
  * `output`: triggers a transcode using all the options set up to this
102
- point.
103
- * `scan`: triggers a title scan and returns a `Hash`-like structure
104
- of {HandBrake::Title} objects describing the contents of the
105
- input. The hash is indexed by title number.
102
+ point. No return value.
103
+ * `scan`: triggers a title scan and returns either a {HandBrake::Disc}
104
+ (for all titles) or a {HandBrake::Title} (for a single title).
106
105
  * `update`: returns true or false depending on whether the version of
107
106
  HandBrakeCLI in use is up to date.
108
107
  * `preset_list`: returns a hash containing all the known presets and
@@ -122,9 +121,8 @@ along different paths. E.g.:
122
121
  # TV
123
122
  project.preset('High Profile').output('project-tv.m4v')
124
123
 
125
- To put it more technically, the chain object returned from each
126
- intermediate configuration step returns an independent copy of the
127
- configuration chain.
124
+ To put it more technically, each intermediate configuration step
125
+ returns an independent copy of the configuration chain.
128
126
 
129
127
  ### Output options
130
128
 
@@ -5,9 +5,10 @@ module HandBrake
5
5
  autoload :VERSION, 'handbrake/version'
6
6
 
7
7
  autoload :CLI, 'handbrake/cli'
8
- autoload :Titles, 'handbrake/titles'
9
- autoload :Title, 'handbrake/titles'
10
- autoload :Chapter, 'handbrake/titles'
8
+ autoload :Disc, 'handbrake/disc'
9
+ autoload :Title, 'handbrake/disc'
10
+ autoload :Titles, 'handbrake/disc'
11
+ autoload :Chapter, 'handbrake/disc'
11
12
 
12
13
  ##
13
14
  # The exception thrown when a file exists that shouldn't.
@@ -68,16 +68,19 @@ module HandBrake
68
68
  # `true`, the file is replaced. If `false`, an exception is
69
69
  # thrown. If `:ignore`, the file is skipped; i.e., HandBrakeCLI
70
70
  # is not invoked.
71
- # @option options [Boolean] :atomic (false) provides a
71
+ # @option options [Boolean, String] :atomic (false) provides a
72
72
  # pseudo-atomic mode for transcoded output. If true, the
73
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.
74
+ # the specified filename if it completes. If the value is
75
+ # literally `true`, the temporary filename is the target
76
+ # filename with `.handbraking` inserted before the extension. If
77
+ # the value is a string, it is interpreted as a path; the
78
+ # temporary file is written to this path instead of in the
79
+ # ultimate target directory. Any `:overwrite` checking will be
80
+ # applied to the target filename both before and after the
81
+ # transcode happens (the temporary file will always be
82
+ # overwritten). This option is intended to aid in writing
83
+ # automatically resumable batch scripts.
81
84
  #
82
85
  # @return [void]
83
86
  def output(filename, options={})
@@ -98,10 +101,15 @@ module HandBrake
98
101
 
99
102
  atomic = options.delete :atomic
100
103
  interim_filename =
101
- if atomic
104
+ case atomic
105
+ when true
102
106
  partial_filename(filename)
103
- else
107
+ when String
108
+ partial_filename(File.join(atomic, File.basename(filename)))
109
+ when false, nil
104
110
  filename
111
+ else
112
+ fail "Unsupported value for :atomic: #{atomic.inspect}"
105
113
  end
106
114
 
107
115
  unless options.empty?
@@ -127,7 +135,7 @@ module HandBrake
127
135
  else
128
136
  true
129
137
  end
130
- FileUtils.mv interim_filename, filename if replace
138
+ FileUtils.mv(interim_filename, filename, :verbose => trace?) if replace
131
139
  end
132
140
  end
133
141
 
@@ -147,13 +155,22 @@ module HandBrake
147
155
  # titles. (HandBrakeCLI defaults to only returning information for
148
156
  # title 1.)
149
157
  #
150
- # @return [Titles]
158
+ # @return [Disc,Title] a {Disc} when scanning for all titles, or a
159
+ # {Title} when scanning for one title.
151
160
  def scan
152
- if arguments.include?('--title')
153
- result = run('--scan')
154
- Titles.from_output(result.output)
161
+ one_title = arguments.include?('--title')
162
+
163
+ args = %w(--scan)
164
+ unless one_title
165
+ args.unshift('--title', '0')
166
+ end
167
+
168
+ disc = Disc.from_output(run(*args).output)
169
+
170
+ if one_title
171
+ disc.titles.values.first
155
172
  else
156
- title(0).scan
173
+ disc
157
174
  end
158
175
  end
159
176
 
@@ -264,7 +281,7 @@ module HandBrake
264
281
 
265
282
  $stderr.puts "Spawning HandBrakeCLI using #{cmd.inspect}" if @cli.trace?
266
283
  IO.popen(cmd) do |io|
267
- while line = io.gets
284
+ while line = io.read(60)
268
285
  output << line
269
286
  $stderr.write(line) if @cli.trace?
270
287
  end
@@ -4,13 +4,12 @@ require 'handbrake'
4
4
 
5
5
  module HandBrake
6
6
  ##
7
- # And enhanced `Hash` which can self-parse the output from
8
- # HandBrakeCLI's `--scan` mode. The keys of this hash will be title
9
- # numbers and the values will be {Title} instances.
7
+ # An object representation of the output from HandBrakeCLI's
8
+ # `--scan` mode.
10
9
  #
11
10
  # @see Title
12
11
  # @see Chapter
13
- class Titles < Hash
12
+ class Disc
14
13
  ##
15
14
  # The HandBrakeCLI scan output from which this instance was
16
15
  # parsed, if available.
@@ -26,18 +25,37 @@ module HandBrake
26
25
  attr_reader :raw_tree
27
26
 
28
27
  ##
29
- # Builds a new {Titles} instance from the output of `HandBrakeCLI
28
+ # The titles in the disc. The keys are the title numbers.
29
+ #
30
+ # @return [Hash<Fixnum, Title>]
31
+ attr_reader :titles
32
+
33
+ ##
34
+ # The name of the disc.
35
+ #
36
+ # @return [String]
37
+ attr_accessor :name
38
+
39
+ def initialize(name=nil)
40
+ @name = name
41
+ @titles = {}
42
+ end
43
+
44
+ ##
45
+ # Builds a new {Disc} instance from the output of `HandBrakeCLI
30
46
  # --scan`.
31
47
  #
32
48
  # @param [String] output the raw contents from the scan
33
- # @return [Titles] a new, completely initialized title catalog
49
+ # @return [Disc] a new, completely initialized title catalog
34
50
  def self.from_output(output)
35
- self.new.tap do |titles|
36
- titles.raw_output = output
37
- titles.raw_tree.children.
51
+ name = File.basename(
52
+ output.split("\n").grep(/hb_scan/).first.scan(/path=([^,]*),/).first.first)
53
+ self.new(name).tap do |disc|
54
+ disc.raw_output = output
55
+ disc.raw_tree.children.
38
56
  collect { |title_node| Title.from_tree(title_node) }.
39
- each { |title| title.collection = titles }.
40
- each { |title| titles[title.number] = title }
57
+ each { |title| title.disc = disc }.
58
+ each { |title| disc.titles[title.number] = title }
41
59
  end
42
60
  end
43
61
 
@@ -54,6 +72,10 @@ module HandBrake
54
72
  @raw_tree = extract_tree
55
73
  end
56
74
 
75
+ def to_yaml_properties
76
+ %w(@name @titles)
77
+ end
78
+
57
79
  private
58
80
 
59
81
  def extract_tree
@@ -100,10 +122,27 @@ module HandBrake
100
122
  end
101
123
  end
102
124
 
125
+ ##
126
+ # Mix-in that provides a class with a constructor that sets any
127
+ # publicly writable properties from values in a hash.
128
+ module ConstructWithProperties
129
+ def initialize(properties={})
130
+ properties.each do |prop, value|
131
+ setter = :"#{prop}="
132
+ if self.respond_to?(setter)
133
+ self.send(setter, value)
134
+ else
135
+ fail("No property #{prop.inspect} in #{self.class}")
136
+ end
137
+ end
138
+ end
139
+ end
140
+
103
141
  ##
104
142
  # Metadata about a single DVD title.
105
143
  class Title
106
144
  include DurationAsSeconds
145
+ include ConstructWithProperties
107
146
 
108
147
  ##
109
148
  # @return [Fixnum] The title number of this title (a positive integer).
@@ -125,8 +164,8 @@ module HandBrake
125
164
  attr_writer :main_feature
126
165
 
127
166
  ##
128
- # @return [Titles] The collection this title belongs to.
129
- attr_accessor :collection
167
+ # @return [Disc] The disc this title belongs to.
168
+ attr_accessor :disc
130
169
 
131
170
  ##
132
171
  # Creates a new instance from the given scan subtree.
@@ -176,6 +215,7 @@ module HandBrake
176
215
  # The metadata about a single chapter in a title of a DVD.
177
216
  class Chapter
178
217
  include DurationAsSeconds
218
+ include ConstructWithProperties
179
219
 
180
220
  ##
181
221
  # @return [String] The duration of the title in the format
@@ -1,5 +1,5 @@
1
1
  module HandBrake
2
2
  ##
3
3
  # The current version
4
- VERSION = '0.2.1'
4
+ VERSION = '0.3.0'
5
5
  end
@@ -163,32 +163,30 @@ module HandBrake
163
163
  end
164
164
  end
165
165
 
166
- context 'true' do
167
- let(:expected_temporary_file) { File.join(tmpdir, "foo.handbraking.m4v") }
168
-
166
+ shared_examples 'atomic enabled' do
169
167
  it 'outputs to the temporary file with ".handbraking" inserted before the extension' do
170
- cli.output(filename, :atomic => true)
168
+ cli.output(filename, :atomic => atomic_option_value)
171
169
  it_should_have_run(expected_temporary_file)
172
170
  end
173
171
 
174
172
  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'))
173
+ cli.output(File.join(tmpdir('a.b.c'), 'foo'), :atomic => atomic_option_value)
174
+ it_should_have_run(expected_no_extension_temporary_filename)
177
175
  end
178
176
 
179
177
  it 'copies the output to the desired filename' do
180
- cli.output(filename, :atomic => true)
178
+ cli.output(filename, :atomic => atomic_option_value)
181
179
  File.read(filename).should == 'This is the file created by --output'
182
180
  end
183
181
 
184
182
  it 'removes the temporary file' do
185
- cli.output(filename, :atomic => true)
183
+ cli.output(filename, :atomic => atomic_option_value)
186
184
  File.exist?(expected_temporary_file).should be_false
187
185
  end
188
186
 
189
187
  it 'overwrites the temporary file if it exists' do
190
188
  FileUtils.touch expected_temporary_file
191
- cli.output(filename, :atomic => true)
189
+ cli.output(filename, :atomic => atomic_option_value)
192
190
  it_should_have_run(expected_temporary_file)
193
191
  end
194
192
 
@@ -196,13 +194,13 @@ module HandBrake
196
194
  describe 'false' do
197
195
  it 'throws an exception if the target filename exists initially' do
198
196
  FileUtils.touch filename
199
- lambda { cli.output(filename, :atomic => true, :overwrite => false) }.
197
+ lambda { cli.output(filename, :atomic => atomic_option_value, :overwrite => false) }.
200
198
  should raise_error(HandBrake::FileExistsError)
201
199
  end
202
200
 
203
201
  it 'throws an exception if the target filename exists after output' do
204
202
  runner.behavior = lambda { FileUtils.touch filename }
205
- lambda { cli.output(filename, :atomic => true, :overwrite => false) }.
203
+ lambda { cli.output(filename, :atomic => atomic_option_value, :overwrite => false) }.
206
204
  should raise_error(HandBrake::FileExistsError)
207
205
  end
208
206
  end
@@ -210,7 +208,7 @@ module HandBrake
210
208
  describe ':ignore' do
211
209
  it 'does nothing if the target filename exists initially' do
212
210
  FileUtils.touch filename
213
- cli.output(filename, :atomic => true, :overwrite => :ignore)
211
+ cli.output(filename, :atomic => atomic_option_value, :overwrite => :ignore)
214
212
  it_should_not_have_run
215
213
  end
216
214
 
@@ -218,7 +216,7 @@ module HandBrake
218
216
  runner.behavior = lambda {
219
217
  File.open(filename, 'w') { |f| f.write 'Other process result' }
220
218
  }
221
- cli.output(filename, :atomic => true, :overwrite => :ignore)
219
+ cli.output(filename, :atomic => atomic_option_value, :overwrite => :ignore)
222
220
  File.read(filename).should == 'Other process result'
223
221
  end
224
222
 
@@ -226,17 +224,38 @@ module HandBrake
226
224
  runner.behavior = lambda {
227
225
  File.open(filename, 'w') { |f| f.write 'Other process result' }
228
226
  }
229
- cli.output(filename, :atomic => true, :overwrite => :ignore)
227
+ cli.output(filename, :atomic => atomic_option_value, :overwrite => :ignore)
230
228
  File.exist?(expected_temporary_file).should be_true
231
229
  end
232
230
  end
233
231
  end
234
232
  end
233
+
234
+ context 'true' do
235
+ let(:atomic_option_value) { true }
236
+ let(:expected_temporary_file) { File.join(tmpdir, "foo.handbraking.m4v") }
237
+ let(:expected_no_extension_temporary_filename) {
238
+ File.join(tmpdir('a.b.c'), 'foo.handbraking')
239
+ }
240
+
241
+ include_examples 'atomic enabled'
242
+ end
243
+
244
+ context 'an alternate path' do
245
+ let(:atomic_option_value) { tmpdir('somewhere/else') }
246
+ let(:expected_temporary_file) { File.join(atomic_option_value, "foo.handbraking.m4v") }
247
+ let(:expected_no_extension_temporary_filename) {
248
+ File.join(atomic_option_value, 'foo.handbraking')
249
+ }
250
+
251
+ include_examples 'atomic enabled'
252
+ end
235
253
  end
236
254
  end
237
255
 
238
256
  describe '#scan' do
239
257
  let(:sample_scan) { File.read(File.expand_path('../sample-titles-scan.err', __FILE__)) }
258
+ let(:single_scan) { File.read(File.expand_path('../single-title-scan.err', __FILE__)) }
240
259
 
241
260
  before do
242
261
  runner.output = sample_scan
@@ -253,8 +272,8 @@ module HandBrake
253
272
  end
254
273
 
255
274
  it 'only scans the specified title if one is specified' do
256
- cli.title(3).scan
257
- runner.actual_arguments.should == %w(--title 3 --scan)
275
+ cli.title(2).scan
276
+ runner.actual_arguments.should == %w(--title 2 --scan)
258
277
  end
259
278
 
260
279
  it 'does not permanently modify the argument list when using the default title setting' do
@@ -262,9 +281,15 @@ module HandBrake
262
281
  cli.arguments.should_not include('--title')
263
282
  end
264
283
 
265
- it 'returns titles from the output' do
266
- # The details for this are tested in titles_spec
267
- cli.scan.size.should == 5
284
+ it 'return a Disc when scanning all titles' do
285
+ # The details for this are tested in disc_spec
286
+ cli.scan.should be_a HandBrake::Disc
287
+ end
288
+
289
+ it 'returns a Title when scanning one title' do
290
+ # The details for this are tested in disc_spec
291
+ runner.output = single_scan
292
+ cli.title(2).scan.should be_a HandBrake::Title
268
293
  end
269
294
  end
270
295
 
@@ -0,0 +1,201 @@
1
+ require File.expand_path('../../spec_helper.rb', __FILE__)
2
+
3
+ module HandBrake
4
+ describe Disc do
5
+ let(:body) { File.read(File.expand_path('../sample-titles-scan.err', __FILE__)) }
6
+ let(:disc) { Disc.from_output(body) }
7
+ let(:titles) { disc.titles }
8
+
9
+ describe '#titles' do
10
+ it 'contains all the titles' do
11
+ titles.size.should == 5
12
+ end
13
+
14
+ it 'indexes the titles correctly' do
15
+ titles[3].number.should == 3
16
+ end
17
+
18
+ it 'is enumerable' do
19
+ titles.collect { |k, v| k }.sort.should == [1, 2, 3, 6, 11]
20
+ end
21
+ end
22
+
23
+ it 'extracts the name' do
24
+ disc.name.should == 'D2'
25
+ end
26
+
27
+ it 'contains a reference to the full output' do
28
+ disc.raw_output.should == body
29
+ end
30
+
31
+ describe "#raw_tree" do
32
+ let(:tree) { disc.raw_tree }
33
+
34
+ it 'has the raw values at the top level of nodes' do
35
+ tree.children.collect { |c| c.name }.should ==
36
+ ['title 1:', 'title 2:', 'title 3:', 'title 6:', 'title 11:']
37
+ end
38
+
39
+ it 'has the raw values at the second level' do
40
+ tree['title 11:'][3].name.should == 'autocrop: 0/0/0/0'
41
+ end
42
+
43
+ it 'has the raw values at the third level' do
44
+ tree['title 3:']['audio tracks:'][1].name.should ==
45
+ '2, Francais (AC3) (Dolby Surround) (iso639-2: fra), 48000Hz, 192000bps'
46
+ end
47
+ end
48
+
49
+ describe 'YAML serialization' do
50
+ describe 'round trip' do
51
+ let(:yaml) { disc.to_yaml }
52
+ let(:reloaded) { YAML.load(yaml) }
53
+
54
+ it 'does not include the raw output' do
55
+ reloaded.raw_output.should be_nil
56
+ end
57
+
58
+ it 'does not include the raw tree' do
59
+ reloaded.raw_tree.should be_nil
60
+ end
61
+
62
+ it 'preserves the name' do
63
+ reloaded.name.should == 'D2'
64
+ end
65
+
66
+ it 'preserves the titles' do
67
+ reloaded.titles.size.should == 5
68
+ end
69
+
70
+ it 'preserves the backreference from a title to the disc' do
71
+ pending "Psych issue #19" if RUBY_VERSION =~ /1.9/
72
+ reloaded.titles[1].disc.should eql(reloaded)
73
+ end
74
+
75
+ it 'preserves the chapters' do
76
+ reloaded.titles[3].chapters.size.should == 13
77
+ end
78
+
79
+ it 'preserves the backreference from a chapter to its title' do
80
+ pending "Psych issue #19" if RUBY_VERSION =~ /1.9/
81
+ title_1 = reloaded.titles[1]
82
+ title_1.chapters[4].title.should eql(title_1)
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ describe Title do
89
+ let(:body) { File.read(File.expand_path('../sample-titles-scan.err', __FILE__)) }
90
+ let(:disc) { Disc.from_output(body) }
91
+ let(:titles) { disc.titles }
92
+
93
+ let(:title_1) { titles[1] }
94
+ let(:title_3) { titles[3] }
95
+
96
+ it 'has a reference to its parent disc' do
97
+ title_1.disc.should be disc
98
+ end
99
+
100
+ describe '#initialize' do
101
+ describe 'without a hash' do
102
+ it 'works' do
103
+ lambda { Title.new }.should_not raise_error
104
+ end
105
+ end
106
+
107
+ describe 'with a hash' do
108
+ it 'can set settable properties' do
109
+ Title.new(:number => 5, :duration => '01:45:35').number.should == 5
110
+ end
111
+
112
+ it 'fails for an unknown property' do
113
+ lambda { Title.new(:foo => 'bar') }.
114
+ should raise_error('No property :foo in HandBrake::Title')
115
+ end
116
+ end
117
+ end
118
+
119
+ describe '#main_feature?' do
120
+ it 'is true when it is' do
121
+ title_1.should be_main_feature
122
+ end
123
+
124
+ it 'is false when it is' do
125
+ title_3.should_not be_main_feature
126
+ end
127
+
128
+ it 'is not a complex value when true, so that serializations are simpler' do
129
+ title_1.main_feature?.should == true
130
+ end
131
+ end
132
+
133
+ it 'has the number' do
134
+ title_1.number.should == 1
135
+ end
136
+
137
+ it 'has the duration' do
138
+ title_3.duration.should == '01:43:54'
139
+ end
140
+
141
+ it 'has the duration in seconds' do
142
+ title_3.seconds.should == 6234
143
+ end
144
+
145
+ it 'has the right number of chapters' do
146
+ title_3.should have(13).chapters
147
+ end
148
+
149
+ it 'has chapters indexed by chapter number' do
150
+ title_3.chapters[2].tap do |c|
151
+ c.duration.should == '00:00:52'
152
+ c.number.should == 2
153
+ end
154
+ end
155
+
156
+ it 'has an array of the chapters in order' do
157
+ title_3.all_chapters.collect { |ch| ch.number }.should == (1..13).to_a
158
+ end
159
+
160
+ describe 'a chapter' do
161
+ let(:chapter) { title_3.chapters[5] }
162
+
163
+ it 'has the duration' do
164
+ chapter.duration.should == '00:03:23'
165
+ end
166
+
167
+ it 'has the duration in seconds' do
168
+ chapter.seconds.should == 203
169
+ end
170
+
171
+ it 'has the number' do
172
+ chapter.number.should == 5
173
+ end
174
+
175
+ it 'has a reference to its parent' do
176
+ chapter.title.should eql title_3
177
+ end
178
+ end
179
+ end
180
+
181
+ describe Chapter do
182
+ describe '#initialize' do
183
+ describe 'without a hash' do
184
+ it 'works' do
185
+ lambda { Chapter.new }.should_not raise_error
186
+ end
187
+ end
188
+
189
+ describe 'with a hash' do
190
+ it 'can set settable properties' do
191
+ Chapter.new(:number => 5, :duration => '01:45:35').duration.should == '01:45:35'
192
+ end
193
+
194
+ it 'fails for an unknown property' do
195
+ lambda { Chapter.new(:foo => 'bar') }.
196
+ should raise_error('No property :foo in HandBrake::Chapter')
197
+ end
198
+ end
199
+ end
200
+ end
201
+ end
@@ -0,0 +1,96 @@
1
+ [21:49:40] hb_init: checking cpu count
2
+ [21:49:40] hb_init: starting libhb thread
3
+ HandBrake rev3736 (2011081199) - Darwin x86_64 - http://handbrake.fr
4
+ 2 CPUs detected
5
+ Opening /Volumes/Arcturan Megafreighter/DVDs/L...
6
+ [21:49:40] hb_scan: path=/Volumes/Arcturan Megafreighter/DVDs/L, title_index=1
7
+ libbluray/bdnav/index_parse.c:157: indx_parse(): error opening /Volumes/Arcturan Megafreighter/DVDs/L/BDMV/index.bdmv
8
+ libbluray/bluray.c:960: nav_get_title_list(/Volumes/Arcturan Megafreighter/DVDs/L) failed (0x102013400)
9
+ [21:49:40] bd: not a bd - trying as a stream/file instead
10
+ libdvdnav: Using dvdnav version 4.1.3
11
+ libdvdread: Using libdvdcss version 1.2.9 for DVD access
12
+ libdvdread: Couldn't find device name.
13
+ libdvdnav: Can't read name block. Probably not a DVD-ROM device.
14
+ libdvdnav: Unable to find map file '/Users/rsutphin/.dvdnav/.map'
15
+ libdvdnav: DVD disk reports itself with Region mask 0x00fe0000. Regions: 1
16
+ libdvdread: Using libdvdcss version 1.2.9 for DVD access
17
+ libdvdread: Couldn't find device name.
18
+ [21:49:40] scan: DVD has 3 title(s)
19
+ [21:49:40] scan: scanning title 1
20
+ [21:49:40] scan: opening IFO for VTS 1
21
+ [21:49:40] scan: duration is 01:21:18 (4878000 ms)
22
+ [21:49:40] pgc_id: 1, pgn: 1: pgc: 0x101605130
23
+ [21:49:40] scan: vts=1, ttn=1, cells=0->22, blocks=58609->58608, 2081952 blocks
24
+ [21:49:40] scan: checking audio 1
25
+ [21:49:40] scan: id=80bd, lang=English (AC3), 3cc=eng ext=0
26
+ [21:49:40] scan: checking audio 2
27
+ [21:49:40] scan: id=81bd, lang=Francais (AC3), 3cc=fra ext=0
28
+ [21:49:40] scan: title 1 has 23 chapters
29
+ [21:49:40] scan: chap 1 c=0->0, b=58609->108062 (49454), 120223 ms
30
+ [21:49:40] scan: chap 2 c=1->1, b=108063->181132 (73070), 148385 ms
31
+ [21:49:40] scan: chap 3 c=2->2, b=181133->265640 (84508), 208487 ms
32
+ [21:49:40] scan: chap 4 c=3->3, b=265641->342075 (76435), 199636 ms
33
+ [21:49:40] scan: chap 5 c=4->4, b=342076->464043 (121968), 322732 ms
34
+ [21:49:40] scan: chap 6 c=5->5, b=464044->528308 (64265), 167554 ms
35
+ [21:49:40] scan: chap 7 c=6->6, b=528309->613576 (85268), 297605 ms
36
+ [21:49:40] scan: chap 8 c=7->7, b=613577->698083 (84507), 237727 ms
37
+ [21:49:40] scan: chap 9 c=8->8, b=698084->832431 (134348), 291828 ms
38
+ [21:49:40] scan: chap 10 c=9->9, b=832432->950393 (117962), 271647 ms
39
+ [21:49:40] scan: chap 11 c=10->10, b=950394->1067913 (117520), 281843 ms
40
+ [21:49:40] scan: chap 12 c=11->11, b=1067914->1163132 (95219), 212671 ms
41
+ [21:49:40] scan: chap 13 c=12->12, b=1163133->1282848 (119716), 276599 ms
42
+ [21:49:40] scan: chap 14 c=13->13, b=1282849->1330562 (47714), 120309 ms
43
+ [21:49:40] scan: chap 15 c=14->14, b=1330563->1476681 (146119), 301613 ms
44
+ [21:49:40] scan: chap 16 c=15->15, b=1476682->1577253 (100572), 205434 ms
45
+ [21:49:40] scan: chap 17 c=16->16, b=1577254->1650064 (72811), 185610 ms
46
+ [21:49:40] scan: chap 18 c=17->17, b=1650065->1744727 (94663), 195639 ms
47
+ [21:49:40] scan: chap 19 c=18->18, b=1744728->1904668 (159941), 328863 ms
48
+ [21:49:40] scan: chap 20 c=19->19, b=1904669->2018698 (114030), 252789 ms
49
+ [21:49:40] scan: chap 21 c=20->20, b=2018699->2105949 (87251), 179543 ms
50
+ [21:49:40] scan: chap 22 c=21->21, b=2105950->2140482 (34533), 64239 ms
51
+ [21:49:40] scan: chap 23 c=22->22, b=58531->58608 (78), 7013 ms
52
+ [21:49:40] scan: aspect = 0
53
+ [21:49:40] scan: decoding previews for title 1
54
+ libdvdnav: DVD disk reports itself with Region mask 0x00fe0000. Regions: 1
55
+ [21:49:40] scan: title angle(s) 1
56
+ [21:49:40] scan: audio 0x80bd: AC-3, rate=48000Hz, bitrate=192000 English (AC3) (2.0 ch)
57
+ [21:49:40] scan: audio 0x81bd: AC-3, rate=48000Hz, bitrate=192000 Francais (AC3) (2.0 ch)
58
+ Scanning title 1 of 3...
59
+ [21:49:40] scan: 10 previews, 720x480, 23.976 fps, autocrop = 0/0/8/4, aspect 4:3, PAR 8:9
60
+ [21:49:40] scan: title (0) job->width:624, job->height:480
61
+ [21:49:40] libhb: scan thread found 1 valid title(s)
62
+ + title 1:
63
+ + vts 1, ttn 1, cells 0->22 (2081952 blocks)
64
+ + duration: 01:21:18
65
+ + size: 720x480, pixel aspect: 8/9, display aspect: 1.33, 23.976 fps
66
+ + autocrop: 0/0/8/4
67
+ + chapters:
68
+ + 1: cells 0->0, 49454 blocks, duration 00:02:00
69
+ + 2: cells 1->1, 73070 blocks, duration 00:02:28
70
+ + 3: cells 2->2, 84508 blocks, duration 00:03:28
71
+ + 4: cells 3->3, 76435 blocks, duration 00:03:20
72
+ + 5: cells 4->4, 121968 blocks, duration 00:05:23
73
+ + 6: cells 5->5, 64265 blocks, duration 00:02:48
74
+ + 7: cells 6->6, 85268 blocks, duration 00:04:58
75
+ + 8: cells 7->7, 84507 blocks, duration 00:03:58
76
+ + 9: cells 8->8, 134348 blocks, duration 00:04:52
77
+ + 10: cells 9->9, 117962 blocks, duration 00:04:32
78
+ + 11: cells 10->10, 117520 blocks, duration 00:04:42
79
+ + 12: cells 11->11, 95219 blocks, duration 00:03:33
80
+ + 13: cells 12->12, 119716 blocks, duration 00:04:37
81
+ + 14: cells 13->13, 47714 blocks, duration 00:02:00
82
+ + 15: cells 14->14, 146119 blocks, duration 00:05:02
83
+ + 16: cells 15->15, 100572 blocks, duration 00:03:25
84
+ + 17: cells 16->16, 72811 blocks, duration 00:03:06
85
+ + 18: cells 17->17, 94663 blocks, duration 00:03:16
86
+ + 19: cells 18->18, 159941 blocks, duration 00:05:29
87
+ + 20: cells 19->19, 114030 blocks, duration 00:04:13
88
+ + 21: cells 20->20, 87251 blocks, duration 00:03:00
89
+ + 22: cells 21->21, 34533 blocks, duration 00:01:04
90
+ + 23: cells 22->22, 78 blocks, duration 00:00:07
91
+ + audio tracks:
92
+ + 1, English (AC3) (2.0 ch) (iso639-2: eng), 48000Hz, 192000bps
93
+ + 2, Francais (AC3) (2.0 ch) (iso639-2: fra), 48000Hz, 192000bps
94
+ + subtitle tracks:
95
+ + 1, Closed Captions (iso639-2: eng) (Text)(CC)
96
+ HandBrake has exited.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: handbrake
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-08-14 00:00:00.000000000Z
12
+ date: 2011-08-17 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rubytree
16
- requirement: &2153698700 !ruby/object:Gem::Requirement
16
+ requirement: &2152701940 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 0.8.1
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *2153698700
24
+ version_requirements: *2152701940
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rspec
27
- requirement: &2153698200 !ruby/object:Gem::Requirement
27
+ requirement: &2152701440 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '2.5'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *2153698200
35
+ version_requirements: *2152701440
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rake
38
- requirement: &2153697740 !ruby/object:Gem::Requirement
38
+ requirement: &2152700980 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: 0.9.0
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *2153697740
46
+ version_requirements: *2152700980
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: yard
49
- requirement: &2153697280 !ruby/object:Gem::Requirement
49
+ requirement: &2152700520 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
@@ -54,7 +54,7 @@ dependencies:
54
54
  version: 0.7.0
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *2153697280
57
+ version_requirements: *2152700520
58
58
  description: A lightweight literate ruby wrapper for HandBrakeCLI, the command-line
59
59
  interface for the HandBrake video transcoder.
60
60
  email:
@@ -76,12 +76,13 @@ files:
76
76
  - hrirb
77
77
  - lib/handbrake.rb
78
78
  - lib/handbrake/cli.rb
79
- - lib/handbrake/titles.rb
79
+ - lib/handbrake/disc.rb
80
80
  - lib/handbrake/version.rb
81
81
  - spec/handbrake/cli_spec.rb
82
+ - spec/handbrake/disc_spec.rb
82
83
  - spec/handbrake/sample-preset-list.out
83
84
  - spec/handbrake/sample-titles-scan.err
84
- - spec/handbrake/titles_spec.rb
85
+ - spec/handbrake/single-title-scan.err
85
86
  - spec/handbrake/version_spec.rb
86
87
  - spec/spec_helper.rb
87
88
  homepage: https://github.com/rsutphin/handbrake.rb
@@ -110,8 +111,9 @@ specification_version: 3
110
111
  summary: A ruby wrapper for HandBrakeCLI
111
112
  test_files:
112
113
  - spec/handbrake/cli_spec.rb
114
+ - spec/handbrake/disc_spec.rb
113
115
  - spec/handbrake/sample-preset-list.out
114
116
  - spec/handbrake/sample-titles-scan.err
115
- - spec/handbrake/titles_spec.rb
117
+ - spec/handbrake/single-title-scan.err
116
118
  - spec/handbrake/version_spec.rb
117
119
  - spec/spec_helper.rb
@@ -1,115 +0,0 @@
1
- require File.expand_path('../../spec_helper.rb', __FILE__)
2
-
3
- module HandBrake
4
- describe Titles do
5
- let(:body) { File.read(File.expand_path('../sample-titles-scan.err', __FILE__)) }
6
- let(:titles) { Titles.from_output(body) }
7
-
8
- it 'contains all the titles' do
9
- titles.size.should == 5
10
- end
11
-
12
- it 'indexes the titles correctly' do
13
- titles[3].number.should == 3
14
- end
15
-
16
- it 'is enumerable' do
17
- titles.collect { |k, v| k }.sort.should == [1, 2, 3, 6, 11]
18
- end
19
-
20
- it 'contains a reference to the full output' do
21
- titles.raw_output.should == body
22
- end
23
-
24
- describe "#raw_tree" do
25
- let(:tree) { titles.raw_tree }
26
-
27
- it 'has the raw values at the top level of nodes' do
28
- tree.children.collect { |c| c.name }.should ==
29
- ['title 1:', 'title 2:', 'title 3:', 'title 6:', 'title 11:']
30
- end
31
-
32
- it 'has the raw values at the second level' do
33
- tree['title 11:'][3].name.should == 'autocrop: 0/0/0/0'
34
- end
35
-
36
- it 'has the raw values at the third level' do
37
- tree['title 3:']['audio tracks:'][1].name.should ==
38
- '2, Francais (AC3) (Dolby Surround) (iso639-2: fra), 48000Hz, 192000bps'
39
- end
40
- end
41
- end
42
-
43
- describe Title do
44
- let(:body) { File.read(File.expand_path('../sample-titles-scan.err', __FILE__)) }
45
- let(:titles) { Titles.from_output(body) }
46
-
47
- let(:title_1) { titles[1] }
48
- let(:title_3) { titles[3] }
49
-
50
- it 'has a reference to its parent collection' do
51
- title_1.collection.should eql(titles)
52
- end
53
-
54
- describe '#main_feature?' do
55
- it 'is true when it is' do
56
- title_1.should be_main_feature
57
- end
58
-
59
- it 'is false when it is' do
60
- title_3.should_not be_main_feature
61
- end
62
-
63
- it 'is not a complex value when true, so that serializations are simpler' do
64
- title_1.main_feature?.should == true
65
- end
66
- end
67
-
68
- it 'has the number' do
69
- title_1.number.should == 1
70
- end
71
-
72
- it 'has the duration' do
73
- title_3.duration.should == '01:43:54'
74
- end
75
-
76
- it 'has the duration in seconds' do
77
- title_3.seconds.should == 6234
78
- end
79
-
80
- it 'has the right number of chapters' do
81
- title_3.should have(13).chapters
82
- end
83
-
84
- it 'has chapters indexed by chapter number' do
85
- title_3.chapters[2].tap do |c|
86
- c.duration.should == '00:00:52'
87
- c.number.should == 2
88
- end
89
- end
90
-
91
- it 'has an array of the chapters in order' do
92
- title_3.all_chapters.collect { |ch| ch.number }.should == (1..13).to_a
93
- end
94
-
95
- describe 'a chapter' do
96
- let(:chapter) { title_3.chapters[5] }
97
-
98
- it 'has the duration' do
99
- chapter.duration.should == '00:03:23'
100
- end
101
-
102
- it 'has the duration in seconds' do
103
- chapter.seconds.should == 203
104
- end
105
-
106
- it 'has the number' do
107
- chapter.number.should == 5
108
- end
109
-
110
- it 'has a reference to its parent' do
111
- chapter.title.should eql title_3
112
- end
113
- end
114
- end
115
- end