handbrake 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
>
|