handbrake 0.2.1 → 0.3.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,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