handbrake 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/.rvmrc +1 -0
- data/.travis.yml +4 -0
- data/.yardopts +5 -0
- data/CHANGELOG.md +4 -0
- data/Gemfile +15 -0
- data/LICENSE +458 -0
- data/README.md +159 -0
- data/Rakefile +10 -0
- data/handbrake.gemspec +25 -0
- data/hrirb +15 -0
- data/lib/handbrake/cli.rb +206 -0
- data/lib/handbrake/titles.rb +181 -0
- data/lib/handbrake/version.rb +5 -0
- data/lib/handbrake.rb +11 -0
- data/spec/handbrake/cli_spec.rb +162 -0
- data/spec/handbrake/sample-preset-list.out +38 -0
- data/spec/handbrake/sample-titles-scan.err +375 -0
- data/spec/handbrake/titles_spec.rb +88 -0
- data/spec/handbrake/version_spec.rb +11 -0
- data/spec/spec_helper.rb +57 -0
- metadata +125 -0
data/handbrake.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "handbrake/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "handbrake"
|
7
|
+
s.version = HandBrake::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Rhett Sutphin"]
|
10
|
+
s.email = ["rhett@detailedbalance.net"]
|
11
|
+
s.homepage = "https://github.com/rsutphin/handbrake-ruby"
|
12
|
+
s.summary = %q{A ruby wrapper for HandBrakeCLI}
|
13
|
+
s.description = %q{A lightweight literate ruby wrapper for HandBrakeCLI, the command-line interface for the HandBrake video transcoder.}
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
|
20
|
+
s.add_dependency 'rubytree', '~> 0.8.1'
|
21
|
+
|
22
|
+
s.add_development_dependency 'rspec', '~> 2.5'
|
23
|
+
s.add_development_dependency 'rake', '~> 0.9.0'
|
24
|
+
s.add_development_dependency 'yard', '~> 0.7.0'
|
25
|
+
end
|
data/hrirb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
######
|
4
|
+
# Run irb for interactive fiddling with this library from the source
|
5
|
+
|
6
|
+
require 'irb'
|
7
|
+
|
8
|
+
$LOAD_PATH << File.expand_path('../lib', __FILE__)
|
9
|
+
require 'handbrake'
|
10
|
+
|
11
|
+
$cli = HandBrake::CLI.new(:trace => true)
|
12
|
+
|
13
|
+
puts "`$cli` is a HandBrake::CLI instance with tracing on"
|
14
|
+
|
15
|
+
IRB.start(__FILE__)
|
@@ -0,0 +1,206 @@
|
|
1
|
+
require 'handbrake'
|
2
|
+
|
3
|
+
module HandBrake
|
4
|
+
##
|
5
|
+
# The main entry point for this API. See {file:README.md} for usage
|
6
|
+
# examples.
|
7
|
+
class CLI
|
8
|
+
##
|
9
|
+
# The full path (including filename) to the HandBrakeCLI
|
10
|
+
# executable to use.
|
11
|
+
#
|
12
|
+
# @return [String]
|
13
|
+
attr_accessor :bin_path
|
14
|
+
|
15
|
+
##
|
16
|
+
# Set whether trace is enabled.
|
17
|
+
#
|
18
|
+
# @return [Boolean]
|
19
|
+
attr_writer :trace
|
20
|
+
|
21
|
+
##
|
22
|
+
# @param [Hash] options
|
23
|
+
# @option options [String] :bin_path ('HandBrakeCLI') the full
|
24
|
+
# path to the executable to use
|
25
|
+
# @option options [Boolean] :trace (false) whether {#trace?} is
|
26
|
+
# enabled
|
27
|
+
# @option options [#run] :runner (a PopenRunner instance) the class
|
28
|
+
# encapsulating the execution method for HandBrakeCLI. You
|
29
|
+
# shouldn't usually need to replace this.
|
30
|
+
def initialize(options={})
|
31
|
+
@bin_path = options[:bin_path] || 'HandBrakeCLI'
|
32
|
+
@trace = options[:trace].nil? ? false : options[:trace]
|
33
|
+
@runner = options[:runner] || PopenRunner.new(self)
|
34
|
+
|
35
|
+
@args = []
|
36
|
+
end
|
37
|
+
|
38
|
+
##
|
39
|
+
# Ensures that `#dup` produces a separate copy.
|
40
|
+
#
|
41
|
+
# @return [void]
|
42
|
+
def initialize_copy(original)
|
43
|
+
@args = original.instance_eval { @args }.collect { |bit| bit.dup }
|
44
|
+
end
|
45
|
+
|
46
|
+
##
|
47
|
+
# Is trace enabled?
|
48
|
+
#
|
49
|
+
# If it is enabled, all output from HandBrakeCLI will be streamed
|
50
|
+
# to standard error. If not, the output from HandBrakeCLI will
|
51
|
+
# only be printed if there is a detectable error.
|
52
|
+
#
|
53
|
+
# @return [Boolean]
|
54
|
+
def trace?
|
55
|
+
@trace
|
56
|
+
end
|
57
|
+
|
58
|
+
##
|
59
|
+
# Performs a conversion. This method immediately begins the
|
60
|
+
# transcoding process; set all other options first.
|
61
|
+
#
|
62
|
+
# @return [void]
|
63
|
+
def output(filename)
|
64
|
+
run('--output', filename)
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# Performs a title scan. Unlike HandBrakeCLI, if you do not
|
69
|
+
# specify a title, this method will return information for all
|
70
|
+
# titles. (HandBrakeCLI defaults to only returning information for
|
71
|
+
# title 1.)
|
72
|
+
#
|
73
|
+
# @return [Titles]
|
74
|
+
def scan
|
75
|
+
if arguments.include?('--title')
|
76
|
+
result = run('--scan')
|
77
|
+
Titles.from_output(result.output)
|
78
|
+
else
|
79
|
+
title(0).scan
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
##
|
84
|
+
# Checks to see if the `HandBrakeCLI` instance designated by
|
85
|
+
# {#bin_path} is the current version.
|
86
|
+
#
|
87
|
+
# Note that `HandBrakeCLI` will always report that it is up to
|
88
|
+
# date if it can't connect to the update server, so this is not
|
89
|
+
# terribly reliable.
|
90
|
+
#
|
91
|
+
# @return [Boolean]
|
92
|
+
def update
|
93
|
+
result = run('--update')
|
94
|
+
result.output =~ /Your version of HandBrake is up to date./i
|
95
|
+
end
|
96
|
+
|
97
|
+
##
|
98
|
+
# Returns a structure describing the presets that the current
|
99
|
+
# HandBrake install knows about. The structure is a two-level
|
100
|
+
# hash. The keys in the first level are the preset categories. The
|
101
|
+
# keys in the second level are the preset names and the values are
|
102
|
+
# string representations of the arguments for that preset.
|
103
|
+
#
|
104
|
+
# (This method is included for completeness only. This library does
|
105
|
+
# not provide a mechanism to translate the argument lists returned
|
106
|
+
# here into the configuration for a {HandBrake::CLI} instance.)
|
107
|
+
#
|
108
|
+
# @return [Hash]
|
109
|
+
def preset_list
|
110
|
+
result = run('--preset-list')
|
111
|
+
result.output.scan(%r{\< (.*?)\n(.*?)\>}m).inject({}) { |h1, (cat, block)|
|
112
|
+
h1[cat.strip] = block.scan(/\+(.*?):(.*?)\n/).inject({}) { |h2, (name, args)|
|
113
|
+
h2[name.strip] = args.strip
|
114
|
+
h2
|
115
|
+
}
|
116
|
+
h1
|
117
|
+
}
|
118
|
+
end
|
119
|
+
|
120
|
+
##
|
121
|
+
# @private
|
122
|
+
def arguments
|
123
|
+
@args.collect { |req, *rest| ["--#{req.to_s.gsub('_', '-')}", *rest] }.flatten
|
124
|
+
end
|
125
|
+
|
126
|
+
private
|
127
|
+
|
128
|
+
def run(*more_args)
|
129
|
+
@runner.run(arguments.push(*more_args)).tap do |result|
|
130
|
+
unless result.status == 0
|
131
|
+
unless trace?
|
132
|
+
$stderr.puts result.output
|
133
|
+
end
|
134
|
+
raise "HandBrakeCLI execution failed (#{result.status.inspect})"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
##
|
140
|
+
# Copies this CLI instance and appends another command line switch
|
141
|
+
# plus optional arguments.
|
142
|
+
#
|
143
|
+
# This method does not do any validation of the switch name; if
|
144
|
+
# you use an invalid one, HandBrakeCLI will fail when it is
|
145
|
+
# ultimately invoked.
|
146
|
+
#
|
147
|
+
# @return [CLI]
|
148
|
+
def method_missing(name, *args)
|
149
|
+
copy = self.dup
|
150
|
+
copy.instance_eval { @args << [name, *(args.collect { |a| a.to_s })] }
|
151
|
+
copy
|
152
|
+
end
|
153
|
+
|
154
|
+
##
|
155
|
+
# @private
|
156
|
+
# The default runner. Uses `IO.popen` to spawn
|
157
|
+
# HandBrakeCLI. General use of this library does not require
|
158
|
+
# monkeying with this class.
|
159
|
+
class PopenRunner
|
160
|
+
##
|
161
|
+
# @param [CLI] cli_instance the {CLI} instance whose configuration to share
|
162
|
+
def initialize(cli_instance)
|
163
|
+
@cli = cli_instance
|
164
|
+
end
|
165
|
+
|
166
|
+
# Some notes on popen options
|
167
|
+
# - IO.popen on 1.9.2 is much more elegant than on 1.8.7
|
168
|
+
# (it lets you pass spawn args directly instead of using a
|
169
|
+
# subshell, so you can more cleanly pass args to the
|
170
|
+
# executable and redirect streams)
|
171
|
+
# - Open3.popen3 does not let you get the status
|
172
|
+
# - Open4.popen4 does not seem to stream the output and error
|
173
|
+
# and hangs when the child process fills some buffer
|
174
|
+
# Hence, this implementation:
|
175
|
+
|
176
|
+
##
|
177
|
+
# @param [Array<String>] arguments the arguments to pass to HandBrakeCLI
|
178
|
+
# @return [RunnerResult]
|
179
|
+
def run(arguments)
|
180
|
+
output = ''
|
181
|
+
|
182
|
+
cmd = "'" + arguments.unshift(@cli.bin_path).join("' '") + "' 2>&1"
|
183
|
+
|
184
|
+
$stderr.puts "Spawning HandBrakeCLI using #{cmd.inspect}" if @cli.trace?
|
185
|
+
IO.popen(cmd) do |io|
|
186
|
+
while line = io.gets
|
187
|
+
output << line
|
188
|
+
$stderr.puts(line.chomp) if @cli.trace?
|
189
|
+
end
|
190
|
+
end
|
191
|
+
RunnerResult.new(output, $?)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
##
|
196
|
+
# @private
|
197
|
+
# The raw result of one execution of HandBrakeCLI.
|
198
|
+
#
|
199
|
+
# General use of the library will not require use of this class.
|
200
|
+
#
|
201
|
+
# @attr [String] output a string containing the combined output
|
202
|
+
# and error streams from the run
|
203
|
+
# @attr [#to_i] status the process exit status for the run
|
204
|
+
RunnerResult = Struct.new(:output, :status)
|
205
|
+
end
|
206
|
+
end
|
@@ -0,0 +1,181 @@
|
|
1
|
+
require 'tree'
|
2
|
+
|
3
|
+
require 'handbrake'
|
4
|
+
|
5
|
+
module HandBrake
|
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.
|
10
|
+
#
|
11
|
+
# @see Title
|
12
|
+
# @see Chapter
|
13
|
+
class Titles < Hash
|
14
|
+
##
|
15
|
+
# The HandBrakeCLI scan output from which this instance was
|
16
|
+
# parsed, if available.
|
17
|
+
#
|
18
|
+
# @return [String,nil]
|
19
|
+
attr_reader :raw_output
|
20
|
+
|
21
|
+
##
|
22
|
+
# A tree representing the indented output at the end of the
|
23
|
+
# HandBrakeCLI scan output, if available.
|
24
|
+
#
|
25
|
+
# @return [String,nil]
|
26
|
+
attr_reader :raw_tree
|
27
|
+
|
28
|
+
##
|
29
|
+
# Builds a new {Titles} instance from the output of `HandBrakeCLI
|
30
|
+
# --scan`.
|
31
|
+
#
|
32
|
+
# @param [String] output the raw contents from the scan
|
33
|
+
# @return [Titles] a new, completely initialized title catalog
|
34
|
+
def self.from_output(output)
|
35
|
+
self.new.tap do |titles|
|
36
|
+
titles.raw_output = output
|
37
|
+
titles.raw_tree.children.
|
38
|
+
collect { |title_node| Title.from_tree(title_node) }.
|
39
|
+
each { |title| titles[title.number] = title }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
##
|
44
|
+
# Initializes the {#raw_output} and {#raw_tree} attributes from
|
45
|
+
# the given HandBrakeCLI output. Does not modify the contents of
|
46
|
+
# the hash.
|
47
|
+
#
|
48
|
+
# @param [String] output raw contents from a HandBrakeCLI title
|
49
|
+
# scan
|
50
|
+
# @return [void]
|
51
|
+
def raw_output=(output)
|
52
|
+
@raw_output = output
|
53
|
+
@raw_tree = extract_tree
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def extract_tree
|
59
|
+
split_blocks(
|
60
|
+
raw_output.split("\n").grep(/^\s*\+/), ''
|
61
|
+
).inject(Tree::TreeNode.new('__root__')) do |root, block|
|
62
|
+
root << read_node(block, '')
|
63
|
+
root
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def split_blocks(lines, indent_level)
|
68
|
+
lines.inject([]) do |blocks, line|
|
69
|
+
blocks << [] if line =~ /^#{indent_level}\+/
|
70
|
+
|
71
|
+
blocks.last << line
|
72
|
+
blocks
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def read_node(node_lines, indent_level)
|
77
|
+
next_indent = indent_level + ' '
|
78
|
+
split_blocks(
|
79
|
+
node_lines[1..-1], next_indent
|
80
|
+
).inject(Tree::TreeNode.new(node_lines.first[(2 + indent_level.size)..-1])) do |node, block|
|
81
|
+
node << read_node(block, next_indent)
|
82
|
+
node
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
##
|
88
|
+
# Provides a {#seconds} method for an object which has a `duration`
|
89
|
+
# property whose value is a string of the format "hh:mm:ss"
|
90
|
+
module DurationAsSeconds
|
91
|
+
##
|
92
|
+
# The number of seconds described by the duration. E.g., if the
|
93
|
+
# duration were `"1:02:42"`, this method would return `3762`.
|
94
|
+
#
|
95
|
+
# @return [Fixnum]
|
96
|
+
def seconds
|
97
|
+
@seconds ||= duration.split(':').collect(&:to_i).reverse.
|
98
|
+
inject([1, 0]) { |(m, sum), i| [m * 60, sum + i * m] }.last
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
##
|
103
|
+
# Metadata about a single DVD title.
|
104
|
+
class Title
|
105
|
+
include DurationAsSeconds
|
106
|
+
|
107
|
+
##
|
108
|
+
# @return [Fixnum] The title number of this title (a positive integer).
|
109
|
+
attr_accessor :number
|
110
|
+
|
111
|
+
##
|
112
|
+
# @return [String] The duration of the title in the format
|
113
|
+
# "hh:mm:ss"
|
114
|
+
attr_accessor :duration
|
115
|
+
|
116
|
+
##
|
117
|
+
# @return [Array<Chapter>] The chapters into which the title is
|
118
|
+
# divided.
|
119
|
+
attr_writer :chapters
|
120
|
+
|
121
|
+
##
|
122
|
+
# @return [Boolean] Whether HandBrake considers this title the
|
123
|
+
# "main feature".
|
124
|
+
attr_writer :main_feature
|
125
|
+
|
126
|
+
##
|
127
|
+
# Creates a new instance from the given scan subtree.
|
128
|
+
#
|
129
|
+
# @see Titles.from_output
|
130
|
+
# @param [Tree::TreeNode] title_node
|
131
|
+
# @return [Title] a new, fully initialized instance
|
132
|
+
def self.from_tree(title_node)
|
133
|
+
self.new.tap do |title|
|
134
|
+
title.number = title_node.name.scan(/title (\d+)/).first.first.to_i
|
135
|
+
title.duration = title_node.children.
|
136
|
+
detect { |c| c.name =~ /duration/ }.name.
|
137
|
+
scan(/duration: (\d\d:\d\d:\d\d)/).first.first
|
138
|
+
title.chapters = title_node['chapters:'].children.
|
139
|
+
collect { |ch_node| Chapter.from_tree(ch_node) }
|
140
|
+
title.main_feature = title_node.children.detect { |c| c.name =~ /Main Feature/ }
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
##
|
145
|
+
# @return [Boolean] Whether HandBrake considers this title the
|
146
|
+
# "main feature".
|
147
|
+
def main_feature?
|
148
|
+
@main_feature
|
149
|
+
end
|
150
|
+
|
151
|
+
##
|
152
|
+
# @return [Array<Chapter>] The chapters into which the title is
|
153
|
+
# divided.
|
154
|
+
def chapters
|
155
|
+
@chapters ||= []
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
##
|
160
|
+
# The metadata about a single chapter in a title of a DVD.
|
161
|
+
class Chapter
|
162
|
+
include DurationAsSeconds
|
163
|
+
|
164
|
+
##
|
165
|
+
# @return [String] The duration of the title in the format
|
166
|
+
# "hh:mm:ss"
|
167
|
+
attr_accessor :duration
|
168
|
+
|
169
|
+
##
|
170
|
+
# Creates a new instance from the given title subtree.
|
171
|
+
#
|
172
|
+
# @see Title.from_tree
|
173
|
+
# @param [Tree::TreeNode] chapter_node
|
174
|
+
# @return [Chapter] a new, fully initialized instance
|
175
|
+
def self.from_tree(chapter_node)
|
176
|
+
self.new.tap do |ch|
|
177
|
+
ch.duration = chapter_node.name.scan(/duration (\d\d:\d\d:\d\d)/).first.first
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
data/lib/handbrake.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
##
|
2
|
+
# @see file:README.md
|
3
|
+
# @see CLI The main class
|
4
|
+
module HandBrake
|
5
|
+
autoload :VERSION, 'handbrake/version'
|
6
|
+
|
7
|
+
autoload :CLI, 'handbrake/cli'
|
8
|
+
autoload :Titles, 'handbrake/titles'
|
9
|
+
autoload :Title, 'handbrake/titles'
|
10
|
+
autoload :Chapter, 'handbrake/titles'
|
11
|
+
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
require File.expand_path('../../spec_helper.rb', __FILE__)
|
2
|
+
|
3
|
+
module HandBrake
|
4
|
+
describe CLI do
|
5
|
+
describe "#bin_path" do
|
6
|
+
it "looks on the path by default" do
|
7
|
+
HandBrake::CLI.new.bin_path.should == "HandBrakeCLI"
|
8
|
+
end
|
9
|
+
|
10
|
+
it "is the specified value when specified" do
|
11
|
+
HandBrake::CLI.new(:bin_path => '/Applications/HandBrakeCLI').bin_path.
|
12
|
+
should == '/Applications/HandBrakeCLI'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "#trace?" do
|
17
|
+
it "is false by default" do
|
18
|
+
HandBrake::CLI.new.trace?.should == false
|
19
|
+
end
|
20
|
+
|
21
|
+
it "can be set" do
|
22
|
+
HandBrake::CLI.new(:trace => true).trace?.should == true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "building a command" do
|
27
|
+
let(:cli) { HandBrake::CLI.new }
|
28
|
+
|
29
|
+
it "works for a parameter without an argument" do
|
30
|
+
cli.markers.arguments.should == %w(--markers)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "works for a parameter with an argument" do
|
34
|
+
cli.quality('0.8').arguments.should == %w(--quality 0.8)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "works with a parameter with a dashed name" do
|
38
|
+
cli.native_language('eng').arguments.should == %w(--native-language eng)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "can chain parameters" do
|
42
|
+
cli.previews('15').format('avi').large_file.title('3').arguments.should ==
|
43
|
+
%w(--previews 15 --format avi --large-file --title 3)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "stringifies arguments" do
|
47
|
+
cli.previews(18).arguments.should == %w(--previews 18)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "can produce separate forks of the command" do
|
51
|
+
base = cli.input('/foo/bar')
|
52
|
+
base.quality('0.53').format('avi').arguments.should ==
|
53
|
+
%w(--input /foo/bar --quality 0.53 --format avi)
|
54
|
+
base.title('6').ipod_atom.arguments.should ==
|
55
|
+
%w(--input /foo/bar --title 6 --ipod-atom)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe 'execution' do
|
60
|
+
let(:cli) { HandBrake::CLI.new(:runner => runner) }
|
61
|
+
let(:runner) { HandBrake::Spec::StaticRunner.new }
|
62
|
+
|
63
|
+
describe 'default runner' do
|
64
|
+
let(:cli) { HandBrake::CLI.new }
|
65
|
+
|
66
|
+
before do
|
67
|
+
`which HandBrakeCLI`
|
68
|
+
unless $? == 0
|
69
|
+
pending 'HandBrakeCLI is not on the path'
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'works' do
|
74
|
+
lambda { cli.update }.should_not raise_error
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe '#output' do
|
79
|
+
it 'uses the --output argument' do
|
80
|
+
cli.output('/foo/bar.m4v')
|
81
|
+
runner.actual_arguments.should == %w(--output /foo/bar.m4v)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe '#scan' do
|
86
|
+
let(:sample_scan) { File.read(File.expand_path('../sample-titles-scan.err', __FILE__)) }
|
87
|
+
|
88
|
+
before do
|
89
|
+
runner.output = sample_scan
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'uses the --scan argument' do
|
93
|
+
cli.scan
|
94
|
+
runner.actual_arguments.last.should == '--scan'
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'defaults to scanning all titles' do
|
98
|
+
cli.scan
|
99
|
+
runner.actual_arguments.should == %w(--title 0 --scan)
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'only scans the specified title if one is specified' do
|
103
|
+
cli.title(3).scan
|
104
|
+
runner.actual_arguments.should == %w(--title 3 --scan)
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'does not permanently modify the argument list when using the default title setting' do
|
108
|
+
cli.scan
|
109
|
+
cli.arguments.should_not include('--title')
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'returns titles from the output' do
|
113
|
+
# The details for this are tested in titles_spec
|
114
|
+
cli.scan.size.should == 5
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe '#update' do
|
119
|
+
it 'uses the --update argument' do
|
120
|
+
cli.update
|
121
|
+
runner.actual_arguments.should == %w(--update)
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'returns true if the thing is up to date' do
|
125
|
+
runner.output = 'Your version of HandBrake is up to date.'
|
126
|
+
cli.update.should be_true
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'returns false if the executable is out of date' do
|
130
|
+
runner.output = 'You are using an old version of HandBrake.'
|
131
|
+
cli.update.should be_false
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe '#preset_list' do
|
136
|
+
let(:sample_presets) { File.read(File.expand_path('../sample-preset-list.out', __FILE__)) }
|
137
|
+
|
138
|
+
before do
|
139
|
+
runner.output = sample_presets
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'uses the --preset-list argument' do
|
143
|
+
cli.preset_list
|
144
|
+
runner.actual_arguments.should == %w(--preset-list)
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'returns a hash containing all the categories' do
|
148
|
+
cli.preset_list.keys.sort.should == %w(Apple Legacy Regular)
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'returns a hash containing the presets in each category' do
|
152
|
+
cli.preset_list['Regular'].keys.sort.should == ['High Profile', 'Normal']
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'returns a hash containing the actual args for a particular preset' do
|
156
|
+
cli.preset_list['Regular']['Normal'].should ==
|
157
|
+
'-e x264 -q 20.0 -a 1 -E faac -B 160 -6 dpl2 -R Auto -D 0.0 -f mp4 --strict-anamorphic -m -x ref=2:bframes=2:subme=6:mixed-refs=0:weightb=0:8x8dct=0:trellis=0'
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
|
2
|
+
< Apple
|
3
|
+
|
4
|
+
+ Universal: -e x264 -q 20.0 -a 1,1 -E faac,copy:ac3 -B 160,160 -6 dpl2,auto -R Auto,Auto -D 0.0,0.0 -f mp4 -X 720 --loose-anamorphic -m -x cabac=0:ref=2:me=umh:bframes=0:weightp=0:8x8dct=0:trellis=0:subme=6
|
5
|
+
|
6
|
+
+ iPod: -e x264 -b 700 -a 1 -E faac -B 160 -6 dpl2 -R Auto -D 0.0 -f mp4 -I -X 320 -m -x level=30:bframes=0:weightp=0:cabac=0:ref=1:vbv-maxrate=768:vbv-bufsize=2000:analyse=all:me=umh:no-fast-pskip=1:subme=6:8x8dct=0:trellis=0
|
7
|
+
|
8
|
+
+ iPhone & iPod Touch: -e x264 -q 20.0 -a 1 -E faac -B 128 -6 dpl2 -R Auto -D 0.0 -f mp4 -X 480 -m -x cabac=0:ref=2:me=umh:bframes=0:weightp=0:subme=6:8x8dct=0:trellis=0
|
9
|
+
|
10
|
+
+ iPhone 4: -e x264 -q 20.0 -r 29.97 --pfr -a 1 -E faac -B 160 -6 dpl2 -R Auto -D 0.0 -f mp4 -4 -X 960 --loose-anamorphic -m
|
11
|
+
|
12
|
+
+ iPad: -e x264 -q 20.0 -r 29.97 --pfr -a 1 -E faac -B 160 -6 dpl2 -R Auto -D 0.0 -f mp4 -4 -X 1024 --loose-anamorphic -m
|
13
|
+
|
14
|
+
+ AppleTV: -e x264 -q 20.0 -a 1,1 -E faac,copy:ac3 -B 160,160 -6 dpl2,auto -R Auto,Auto -D 0.0,0.0 -f mp4 -4 -X 960 --loose-anamorphic -m -x cabac=0:ref=2:me=umh:b-pyramid=none:b-adapt=2:weightb=0:trellis=0:weightp=0:vbv-maxrate=9500:vbv-bufsize=9500
|
15
|
+
|
16
|
+
+ AppleTV 2: -e x264 -q 20.0 -r 29.97 --pfr -a 1,1 -E faac,copy:ac3 -B 160,160 -6 dpl2,auto -R Auto,Auto -D 0.0,0.0 -f mp4 -4 -X 1280 --loose-anamorphic -m
|
17
|
+
|
18
|
+
>
|
19
|
+
|
20
|
+
< Regular
|
21
|
+
|
22
|
+
+ Normal: -e x264 -q 20.0 -a 1 -E faac -B 160 -6 dpl2 -R Auto -D 0.0 -f mp4 --strict-anamorphic -m -x ref=2:bframes=2:subme=6:mixed-refs=0:weightb=0:8x8dct=0:trellis=0
|
23
|
+
|
24
|
+
+ High Profile: -e x264 -q 20.0 -a 1,1 -E faac,copy:ac3 -B 160,160 -6 dpl2,auto -R Auto,Auto -D 0.0,0.0 -f mp4 --detelecine --decomb --loose-anamorphic -m -x b-adapt=2:rc-lookahead=50
|
25
|
+
|
26
|
+
>
|
27
|
+
|
28
|
+
< Legacy
|
29
|
+
|
30
|
+
+ Classic: -b 1000 -a 1 -E faac -B 160 -6 dpl2 -R Auto -D 0.0 -f mp4
|
31
|
+
|
32
|
+
+ AppleTV Legacy: -e x264 -b 2500 -a 1,1 -E faac,copy:ac3 -B 160,160 -6 dpl2,auto -R Auto,Auto -D 0.0,0.0 -f mp4 -4 --strict-anamorphic -m -x ref=1:b-pyramid=none:weightp=0:subme=5:me=umh:no-fast-pskip=1:cabac=0:weightb=0:8x8dct=0:trellis=0
|
33
|
+
|
34
|
+
+ iPhone Legacy: -e x264 -b 960 -a 1 -E faac -B 128 -6 dpl2 -R Auto -D 0.0 -f mp4 -I -X 480 -m -x level=30:cabac=0:ref=1:analyse=all:me=umh:no-fast-pskip=1:psy-rd=0,0:bframes=0:weightp=0:subme=6:8x8dct=0:trellis=0
|
35
|
+
|
36
|
+
+ iPod Legacy: -e x264 -b 1500 -a 1 -E faac -B 160 -6 dpl2 -R Auto -D 0.0 -f mp4 -I -X 640 -m -x level=30:bframes=0:weightp=0:cabac=0:ref=1:vbv-maxrate=1500:vbv-bufsize=2000:analyse=all:me=umh:no-fast-pskip=1:psy-rd=0,0:subme=6:8x8dct=0:trellis=0
|
37
|
+
|
38
|
+
>
|