dvd-converter 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +21 -0
- data/README.markdown +33 -0
- data/bin/dvd-converter +6 -0
- data/dvd-converter.gemspec +26 -0
- data/lib/dvd_converter.rb +10 -0
- data/lib/dvd_converter/audio.rb +39 -0
- data/lib/dvd_converter/command_line.rb +130 -0
- data/lib/dvd_converter/constants.rb +11 -0
- data/lib/dvd_converter/converter.rb +178 -0
- data/lib/dvd_converter/movie.rb +116 -0
- data/lib/dvd_converter/requirements.rb +50 -0
- data/lib/dvd_converter/subtitle.rb +26 -0
- data/lib/dvd_converter/track.rb +53 -0
- metadata +90 -0
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2010 Stephen H. Gerstacker
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
20
|
+
|
21
|
+
|
data/README.markdown
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
This script is a hack to help me convert DVD's to MKV's. It's fragile, so
|
2
|
+
please be careful.
|
3
|
+
|
4
|
+
This is for personal use only. Pirating is illegal.
|
5
|
+
|
6
|
+
|
7
|
+
Dependencies For Ubuntu
|
8
|
+
-----------------------
|
9
|
+
|
10
|
+
For Ubuntu 9.10 and greater, you can run the following commands to get all of your requirements.
|
11
|
+
|
12
|
+
# sudo gem install log4r
|
13
|
+
# sudo add-apt-repository ppa:stebbins/handbrake-snapshots
|
14
|
+
# sudo apt-get update
|
15
|
+
# sudo apt-get install handbrake-cli mkvtoolnix transcode subtitleripper
|
16
|
+
|
17
|
+
Dependencies for Mac OS X
|
18
|
+
-------------------------
|
19
|
+
|
20
|
+
For Mac OS X, I've created packages for [brew][brew]. Follow the links to install [brew][brew] and then run the following:
|
21
|
+
|
22
|
+
# brew install mkvtoolnix
|
23
|
+
# brew install transcode
|
24
|
+
# brew install subtitleripper
|
25
|
+
|
26
|
+
You will still need to install Handbrake CLI by hand. Get it from the [Handbrake CLI download page][handbrake] and copy it `/usr/local/bin`.
|
27
|
+
|
28
|
+
|
29
|
+
Author: Stephen H. Gerstacker <stephen@shortround.net>
|
30
|
+
|
31
|
+
[brew]: http://mxcl.github.com/homebrew/ "Homebrew"
|
32
|
+
[handbrake]: http://handbrake.fr/downloads2.php "HandBrake"
|
33
|
+
|
data/bin/dvd-converter
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'dvd-converter'
|
3
|
+
s.version = '0.2.0'
|
4
|
+
s.date = '2010-12-24'
|
5
|
+
|
6
|
+
s.homepage = 'http://shortround.net/projects/dvd-converter'
|
7
|
+
s.summary = "A set of helper scripts to back up DVD's"
|
8
|
+
s.description = <<-EOS
|
9
|
+
dvd-converter is a set of helper scripts to consistantly back up DVD's
|
10
|
+
use good compression, choice of audio tracks and embedded (not burned in)
|
11
|
+
subtitles.
|
12
|
+
EOS
|
13
|
+
|
14
|
+
s.authors = [ 'Stephen H. Gerstacker' ]
|
15
|
+
s.email = 'stephen@shortround.net'
|
16
|
+
|
17
|
+
s.require_paths = ['lib']
|
18
|
+
s.executables = ['dvd-converter']
|
19
|
+
|
20
|
+
s.has_rdoc = false
|
21
|
+
|
22
|
+
s.add_dependency 'log4r', ['>= 1.1.8']
|
23
|
+
|
24
|
+
s.files = Dir['lib/**/*', 'bin/*', 'dvd-converter.gemspec', 'LICENSE', 'README.markdown']
|
25
|
+
end
|
26
|
+
|
@@ -0,0 +1,10 @@
|
|
1
|
+
$LOAD_PATH.push File.expand_path(File.dirname(__FILE__))
|
2
|
+
|
3
|
+
require 'dvd_converter/converter'
|
4
|
+
require 'dvd_converter/constants'
|
5
|
+
require 'dvd_converter/requirements'
|
6
|
+
require 'dvd_converter/movie'
|
7
|
+
require 'dvd_converter/track'
|
8
|
+
require 'dvd_converter/audio'
|
9
|
+
require 'dvd_converter/subtitle'
|
10
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module DVDConverter
|
2
|
+
|
3
|
+
class Audio
|
4
|
+
|
5
|
+
attr_accessor :audio_type
|
6
|
+
attr_accessor :code
|
7
|
+
attr_accessor :ext
|
8
|
+
attr_accessor :track_id
|
9
|
+
attr_accessor :hex
|
10
|
+
attr_accessor :language
|
11
|
+
|
12
|
+
def initialize(id, hex, language, code, ext)
|
13
|
+
@track_id = id
|
14
|
+
@hex = hex
|
15
|
+
@language = language
|
16
|
+
@code = code
|
17
|
+
@ext = ext
|
18
|
+
|
19
|
+
# Set the Audio Type
|
20
|
+
if @language =~ /dts/i
|
21
|
+
@audio_type = 'dts'
|
22
|
+
elsif @language =~ /ac3/i
|
23
|
+
@audio_type = 'ac3'
|
24
|
+
else
|
25
|
+
@audio_type = 'faac'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def print_description
|
30
|
+
print "+ Audio Track #{@track_id}: "
|
31
|
+
print "#{@hex} - "
|
32
|
+
print "#{@language} - "
|
33
|
+
print "#{@code} - "
|
34
|
+
print "#{@ext}\n"
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/../dvd_converter')
|
3
|
+
|
4
|
+
module DVDConverter
|
5
|
+
|
6
|
+
class CommandLine
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
exit if requirements_failed?
|
10
|
+
parse_options
|
11
|
+
exit if temps_exist?
|
12
|
+
do_conversion
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def do_conversion
|
18
|
+
movie = Movie.new @options[:input]
|
19
|
+
movie.scan_titles
|
20
|
+
|
21
|
+
if @options[:list]
|
22
|
+
movie.print_description
|
23
|
+
elsif @options[:everything]
|
24
|
+
movie.convert_everything @options
|
25
|
+
else
|
26
|
+
movie.convert @options
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def parse_options
|
31
|
+
# Default options
|
32
|
+
@options = {
|
33
|
+
:audio => [],
|
34
|
+
:decomb => false,
|
35
|
+
:detelecine => false,
|
36
|
+
:exclude => [],
|
37
|
+
:everything => false,
|
38
|
+
:input => nil,
|
39
|
+
:list => false,
|
40
|
+
:preview => nil,
|
41
|
+
:subtitles => [],
|
42
|
+
:track => nil
|
43
|
+
}
|
44
|
+
|
45
|
+
# Build the options parser
|
46
|
+
@option_parser = OptionParser.new
|
47
|
+
@option_parser.banner = ''
|
48
|
+
|
49
|
+
@option_parser.separator "Information mode:"
|
50
|
+
@option_parser.separator "Usage: dvd_converter -i SOURCE -l"
|
51
|
+
@option_parser.on('-i', '--input SOURCE', 'The path to the DVD source') do |input|
|
52
|
+
@options[:input] = input
|
53
|
+
end
|
54
|
+
@option_parser.on('-l', '--list', 'List the contents of the DVD') do
|
55
|
+
@options[:list] = true
|
56
|
+
end
|
57
|
+
|
58
|
+
@option_parser.separator ''
|
59
|
+
@option_parser.separator "Conversion mode:"
|
60
|
+
@option_parser.separator "Usage: dvd_converter -i SOURCE -t 1 -a 1,2 [-s 1,2,3] [-p 2] [-d]"
|
61
|
+
@option_parser.on('-t', '--track N', Integer, 'The track number to rip') do |track|
|
62
|
+
@options[:track] = track
|
63
|
+
end
|
64
|
+
@option_parser.on('-a', '--audio x,y,z', Array, 'The audio track(s) to rip') do |audio|
|
65
|
+
@options[:audio] = audio.map { |i| i.to_i }
|
66
|
+
end
|
67
|
+
@option_parser.on('-s', '--subtitles x,y,z', Array, 'The subtitle(s) to rip') do |subtitles|
|
68
|
+
@options[:subtitles] = subtitles.map { |i| i.to_i }
|
69
|
+
end
|
70
|
+
@option_parser.on('--decomb', 'Decomb the video') do
|
71
|
+
@options[:decomb] = true
|
72
|
+
end
|
73
|
+
@option_parser.on('--detelecine', 'Detelecine the video') do
|
74
|
+
@options[:detelecine] = true
|
75
|
+
end
|
76
|
+
@option_parser.on('-p', '--preview N', Integer, 'Preview a chapter instead of a full rip') do |preview|
|
77
|
+
@options[:preview] = p
|
78
|
+
end
|
79
|
+
@option_parser.on('-e', '--everything', 'Instead of individual tracks, just rip everything') do
|
80
|
+
@options[:everything] = true
|
81
|
+
end
|
82
|
+
@option_parser.on('-x', '--exclude x,y,z', Array, 'Skip the tracks given tracks') do |exclude|
|
83
|
+
@options[:exclude] = exclude.map { |i| i.to_i }
|
84
|
+
end
|
85
|
+
|
86
|
+
@option_parser.separator ''
|
87
|
+
@option_parser.separator "Common options:"
|
88
|
+
@option_parser.on('-h', '--help', 'Print this help message') do
|
89
|
+
puts @option_parser
|
90
|
+
exit
|
91
|
+
end
|
92
|
+
|
93
|
+
@option_parser.separator ''
|
94
|
+
|
95
|
+
# Parse the options
|
96
|
+
@option_parser.parse! ARGV
|
97
|
+
|
98
|
+
# Does the input exist?
|
99
|
+
if @options[:input].nil? || !File.exists?(@options[:input])
|
100
|
+
$stderr.puts "A valid input directory is required"
|
101
|
+
exit
|
102
|
+
end
|
103
|
+
|
104
|
+
if !@options[:list] && !@options[:everything]
|
105
|
+
# Check and clean the track
|
106
|
+
if @options[:track].nil?
|
107
|
+
$stderr.puts "A track number is required"
|
108
|
+
exit
|
109
|
+
end
|
110
|
+
|
111
|
+
if @options[:audio].empty?
|
112
|
+
$stderr.puts "At least 1 valid audio track is required"
|
113
|
+
exit
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def requirements_failed?
|
119
|
+
r = Requirements.new
|
120
|
+
return !r.test_requirements
|
121
|
+
end
|
122
|
+
|
123
|
+
def temps_exist?
|
124
|
+
return false
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
@@ -0,0 +1,178 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'log4r'
|
4
|
+
|
5
|
+
module DVDConverter
|
6
|
+
|
7
|
+
class Converter
|
8
|
+
include Log4r
|
9
|
+
|
10
|
+
def initialize(track, audio_tracks, subtitle_tracks = [], options = {})
|
11
|
+
# Store the tracks
|
12
|
+
@track = track
|
13
|
+
@audio_tracks = audio_tracks
|
14
|
+
@subtitle_tracks = subtitle_tracks
|
15
|
+
@options = options
|
16
|
+
|
17
|
+
# Build the environment
|
18
|
+
@temp_dir = "#{TEMP_DIR}-#{track.track_id}"
|
19
|
+
Dir.mkdir @temp_dir
|
20
|
+
|
21
|
+
# Build the logger
|
22
|
+
@logger = Logger.new 'converter_log'
|
23
|
+
|
24
|
+
formatter = PatternFormatter.new :pattern => "Track #{track.track_id}: %m"
|
25
|
+
|
26
|
+
console_out = StdoutOutputter.new 'console'
|
27
|
+
console_out.level = INFO
|
28
|
+
console_out.formatter = formatter
|
29
|
+
@logger.outputters << console_out
|
30
|
+
|
31
|
+
file_out = FileOutputter.new 'file', :filename => File.join(@temp_dir, 'converter.log')
|
32
|
+
file_out.level = DEBUG
|
33
|
+
file_out.formatter = formatter
|
34
|
+
@logger.outputters << file_out
|
35
|
+
end
|
36
|
+
|
37
|
+
def cleanup
|
38
|
+
# Build some paths
|
39
|
+
output_file = File.join @temp_dir, @track.track_id.to_s + '.mkv'
|
40
|
+
merged_file = File.join @temp_dir, @track.track_id.to_s + '-with-subs.mkv'
|
41
|
+
logger_file = File.join @temp_dir, 'converter.log'
|
42
|
+
|
43
|
+
# Move out the files
|
44
|
+
if File.exists? merged_file
|
45
|
+
FileUtils.mv merged_file, "#{@track.track_id}.mkv"
|
46
|
+
else
|
47
|
+
FileUtils.mv output_file, "#{@track.track_id}.mkv"
|
48
|
+
end
|
49
|
+
FileUtils.mv logger_file, "#{@track.track_id}.log"
|
50
|
+
|
51
|
+
# Remove the temp folder
|
52
|
+
FileUtils.rm_rf @temp_dir
|
53
|
+
end
|
54
|
+
|
55
|
+
def convert_movie
|
56
|
+
# Build some paths
|
57
|
+
output_file = File.join @temp_dir, @track.track_id.to_s + '.mkv'
|
58
|
+
|
59
|
+
# Build the command to extract the movie
|
60
|
+
command = "#{HANDBRAKECLI_PATH} -i \"#{@track.movie.source}\" -t #{@track.track_id} "
|
61
|
+
command += "-o \"#{output_file}\" -f mkv "
|
62
|
+
command += "-e x264 -q 0.65 -b 2000 -2 -T -m "
|
63
|
+
command += "-9 " if @options[:detelecine]
|
64
|
+
command += "-5 " if @options[:decomb]
|
65
|
+
command += "-a #{@audio_tracks.map { |a| a.track_id }.join(',')} "
|
66
|
+
command += "-E #{@audio_tracks.map { |a| a.audio_type }.join(',')} "
|
67
|
+
command += "-c #{@options[:preview]} " if @options[:preview]
|
68
|
+
command += "2>&1"
|
69
|
+
|
70
|
+
# Run the command
|
71
|
+
@logger.info "Extracting Track #{@track.track_id}:"
|
72
|
+
@logger.info command
|
73
|
+
|
74
|
+
f = IO.popen(command)
|
75
|
+
bytes = []
|
76
|
+
f.each_byte do |b|
|
77
|
+
if b == 13 || b == 10 # Carriage Return or New Line
|
78
|
+
line = bytes.pack('c*')
|
79
|
+
if line =~ /Encoding: task \d+ of \d+/
|
80
|
+
@logger.info line
|
81
|
+
else
|
82
|
+
@logger.debug line
|
83
|
+
end
|
84
|
+
bytes = []
|
85
|
+
else
|
86
|
+
bytes << b
|
87
|
+
end
|
88
|
+
end
|
89
|
+
@logger.debug bytes.pack('c*') unless bytes.empty?
|
90
|
+
end
|
91
|
+
|
92
|
+
def convert_subtitles
|
93
|
+
output_file = File.join @temp_dir, @track.track_id.to_s + '.mkv'
|
94
|
+
|
95
|
+
@subtitle_tracks.each do |subtitle|
|
96
|
+
# Build some paths
|
97
|
+
subtitle_file = File.join @temp_dir, 'subs-' + subtitle.code + '-' + subtitle.track_id.to_s
|
98
|
+
vobsub_file = File.join @temp_dir, 'vobsubs-' + subtitle.code + '-' + subtitle.track_id.to_s
|
99
|
+
vts_file = File.join @track.movie.source, 'VIDEO_TS', 'VTS_' + sprintf('%02d', @track.track_id) + '_0.IFO'
|
100
|
+
|
101
|
+
# Build the command to convert the subtitle
|
102
|
+
command = "#{SUBTITLE2VOBSUB_PATH} -o \"#{vobsub_file}\" -i "
|
103
|
+
command += "\"#{vts_file}\" "
|
104
|
+
command += "< \"#{subtitle_file}\""
|
105
|
+
|
106
|
+
# Run the command to convert the subtitle
|
107
|
+
@logger.info "Converting Subtitle #{subtitle.track_id}, #{subtitle.code}"
|
108
|
+
@logger.info command
|
109
|
+
|
110
|
+
f = IO.popen(command)
|
111
|
+
f.each_line do |line|
|
112
|
+
@logger.debug line.strip
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def extract_subtitles
|
118
|
+
output_file = File.join @temp_dir, @track.track_id.to_s + '.mkv'
|
119
|
+
|
120
|
+
@subtitle_tracks.each do |subtitle|
|
121
|
+
# Build some paths
|
122
|
+
subtitle_file = File.join @temp_dir, 'subs-' + subtitle.code + '-' + subtitle.track_id.to_s
|
123
|
+
|
124
|
+
# Build the command to extract the subtitle
|
125
|
+
command = "#{TCCAT_PATH} -i \"#{@track.movie.source}\" -T #{@track.track_id},-1 2>/dev/null | "
|
126
|
+
command += "#{TCEXTRACT_PATH} -x ps1 -t vob -a 0x#{subtitle.hex} "
|
127
|
+
command += "> \"#{subtitle_file}\""
|
128
|
+
|
129
|
+
# Run the command to extract the subtitle
|
130
|
+
@logger.info "Extracting Subtitle #{subtitle.track_id}, #{subtitle.code}"
|
131
|
+
@logger.info command
|
132
|
+
|
133
|
+
f = IO.popen(command)
|
134
|
+
f.each_line do |line|
|
135
|
+
@logger.debug line.strip
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def merge_subtitles
|
141
|
+
output_file = File.join @temp_dir, @track.track_id.to_s + '.mkv'
|
142
|
+
merged_file = File.join @temp_dir, @track.track_id.to_s + '-with-subs.mkv'
|
143
|
+
|
144
|
+
# Build the command to merge the subtitles
|
145
|
+
command = "#{MKVMERGE_PATH} -o \"#{merged_file}\" \"#{output_file}\" "
|
146
|
+
|
147
|
+
# Add the subtitles to the command
|
148
|
+
@subtitle_tracks.each do |subtitle|
|
149
|
+
vobsub_file = File.join @temp_dir, 'vobsubs-' + subtitle.code + '-' + subtitle.track_id.to_s + '.idx'
|
150
|
+
command += "--language 0:#{subtitle.code} \"#{vobsub_file}\" "
|
151
|
+
end
|
152
|
+
|
153
|
+
# Run the command to merge the subtitles
|
154
|
+
@logger.info "Merging Subtitles in to movie"
|
155
|
+
@logger.info command
|
156
|
+
|
157
|
+
f = IO.popen(command)
|
158
|
+
f.each_line do |line|
|
159
|
+
@logger.debug line.strip
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def process
|
164
|
+
convert_movie
|
165
|
+
|
166
|
+
unless @subtitle_tracks.empty?
|
167
|
+
extract_subtitles
|
168
|
+
convert_subtitles
|
169
|
+
merge_subtitles
|
170
|
+
end
|
171
|
+
|
172
|
+
cleanup
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
|
@@ -0,0 +1,116 @@
|
|
1
|
+
module DVDConverter
|
2
|
+
|
3
|
+
class Movie
|
4
|
+
|
5
|
+
AUDIO_INFO_REGEX = /scan: id=(.+)bd, lang=(.+), 3cc=(.+) ext=(\d+)/.freeze
|
6
|
+
AUDIO_REGEX = /scan: checking audio (\d+)/.freeze
|
7
|
+
DURATION_REGEX = /scan: duration is (.+) \((\d+) ms\)/
|
8
|
+
IGNORE_TITLE_REGEX = /scan: ignoring title/.freeze
|
9
|
+
TITLE_REGEX = /scan: scanning title (\d+)/.freeze
|
10
|
+
SUBTITLE_INFO_REGEX = /scan: id=(.+)bd, lang=(.+), 3cc=(.+)/.freeze
|
11
|
+
SUBTITLE_REGEX = /scan: checking subtitle (\d+)/.freeze
|
12
|
+
|
13
|
+
attr_accessor :source
|
14
|
+
attr_accessor :tracks
|
15
|
+
|
16
|
+
def initialize(source)
|
17
|
+
# Does the given source exist?
|
18
|
+
unless File.exists? source
|
19
|
+
raise ArgumentError, 'Movie source is not a valid path'
|
20
|
+
end
|
21
|
+
|
22
|
+
# Scan the movie for information
|
23
|
+
@source = source
|
24
|
+
self.tracks = {}
|
25
|
+
end
|
26
|
+
|
27
|
+
def convert(options = {})
|
28
|
+
track = self.tracks[options[:track]]
|
29
|
+
track.convert options
|
30
|
+
end
|
31
|
+
|
32
|
+
def convert_everything(options = {})
|
33
|
+
self.tracks.keys.sort.each do |k|
|
34
|
+
self.tracks[k].convert_everything(options) unless options[:exclude].include?(k)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def print_description
|
39
|
+
self.tracks.keys.sort.each do |id|
|
40
|
+
track = self.tracks[id]
|
41
|
+
track.print_description
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def scan_titles
|
46
|
+
# Set some defaults
|
47
|
+
audio_id = nil
|
48
|
+
id = nil
|
49
|
+
subtitle_id = nil
|
50
|
+
|
51
|
+
# Run the command to pull information
|
52
|
+
f = IO.popen("#{HANDBRAKECLI_PATH} -i \"#{@source}\" -t 0 2>&1")
|
53
|
+
f.each_line do |line|
|
54
|
+
# Did we read a title?
|
55
|
+
match = TITLE_REGEX.match(line)
|
56
|
+
if match
|
57
|
+
id = match[1].to_i
|
58
|
+
self.tracks[id] = Track.new id, self
|
59
|
+
audio_id = nil
|
60
|
+
subtitle_id = nil
|
61
|
+
next
|
62
|
+
end
|
63
|
+
|
64
|
+
# Did we read a duration
|
65
|
+
match = DURATION_REGEX.match(line)
|
66
|
+
if match && id
|
67
|
+
self.tracks[id].duration = match[1]
|
68
|
+
next
|
69
|
+
end
|
70
|
+
|
71
|
+
# Do we ignore the title?
|
72
|
+
match = IGNORE_TITLE_REGEX.match(line)
|
73
|
+
if match && id
|
74
|
+
self.tracks.delete(id)
|
75
|
+
id = nil
|
76
|
+
audio_id = nil
|
77
|
+
subtitle_id = nil
|
78
|
+
next
|
79
|
+
end
|
80
|
+
|
81
|
+
# Did we read a subtitle?
|
82
|
+
match = SUBTITLE_REGEX.match(line)
|
83
|
+
if match && self.tracks[id]
|
84
|
+
subtitle_id = match[1].to_i
|
85
|
+
next
|
86
|
+
end
|
87
|
+
|
88
|
+
# Did we read a subtitle info?
|
89
|
+
match = SUBTITLE_INFO_REGEX.match(line)
|
90
|
+
if match && subtitle_id
|
91
|
+
self.tracks[id].subtitles[subtitle_id] = Subtitle.new subtitle_id, match[1], match[2], match[3]
|
92
|
+
subtitle_id = nil
|
93
|
+
next
|
94
|
+
end
|
95
|
+
|
96
|
+
# Did we read an audio channel
|
97
|
+
match = AUDIO_REGEX.match(line)
|
98
|
+
if match && self.tracks[id]
|
99
|
+
audio_id = match[1].to_i
|
100
|
+
next
|
101
|
+
end
|
102
|
+
|
103
|
+
# Did we read an audio channel info?
|
104
|
+
match = AUDIO_INFO_REGEX.match(line)
|
105
|
+
if match && audio_id
|
106
|
+
self.tracks[id].audio[audio_id] = Audio.new audio_id, match[1], match[2], match[3], match[4]
|
107
|
+
audio_id = nil
|
108
|
+
next
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module DVDConverter
|
2
|
+
|
3
|
+
class Requirements
|
4
|
+
|
5
|
+
def find_command(name)
|
6
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).detect do |directory|
|
7
|
+
path = File.join(directory, name)
|
8
|
+
if File.executable?(path)
|
9
|
+
return path
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
return nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def print_missing
|
17
|
+
puts ''
|
18
|
+
puts 'Some required binaries are missing. Please install them and try again'
|
19
|
+
puts ''
|
20
|
+
puts 'Handbrake: http://handbrake.fr'
|
21
|
+
puts 'mkvtoolnix: http://www.bunkus.org/videotools/mkvtoolnix'
|
22
|
+
puts 'transcode: http://tcforge.berlios.de'
|
23
|
+
puts ''
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_requirements
|
27
|
+
commands = [
|
28
|
+
HANDBRAKECLI_PATH, MKVMERGE_PATH, TCCAT_PATH,
|
29
|
+
TCEXTRACT_PATH, SUBTITLE2VOBSUB_PATH
|
30
|
+
]
|
31
|
+
|
32
|
+
commands.each do |c|
|
33
|
+
print "Looking for #{c}... "
|
34
|
+
if find_command c
|
35
|
+
puts "found!"
|
36
|
+
else
|
37
|
+
puts "missing!"
|
38
|
+
print_missing
|
39
|
+
return false
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
puts ''
|
44
|
+
true
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module DVDConverter
|
2
|
+
class Subtitle
|
3
|
+
|
4
|
+
attr_accessor :code
|
5
|
+
attr_accessor :track_id
|
6
|
+
attr_accessor :hex
|
7
|
+
attr_accessor :language
|
8
|
+
|
9
|
+
def initialize(id, hex, language, code)
|
10
|
+
@track_id = id
|
11
|
+
@hex = hex
|
12
|
+
@language = language
|
13
|
+
@code = code
|
14
|
+
end
|
15
|
+
|
16
|
+
def print_description
|
17
|
+
print "+ Subtitle Track #{@track_id}: "
|
18
|
+
print "#{@hex} - "
|
19
|
+
print "#{@language} - "
|
20
|
+
print "#{@code}\n"
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module DVDConverter
|
2
|
+
|
3
|
+
class Track
|
4
|
+
|
5
|
+
attr_accessor :audio
|
6
|
+
attr_accessor :duration
|
7
|
+
attr_accessor :track_id
|
8
|
+
attr_accessor :movie
|
9
|
+
attr_accessor :subtitles
|
10
|
+
|
11
|
+
def initialize(id, movie)
|
12
|
+
self.track_id = id
|
13
|
+
self.movie = movie
|
14
|
+
self.audio = {}
|
15
|
+
self.subtitles = {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def convert(options = {})
|
19
|
+
as = options[:audio].map { |a| self.audio[a] }
|
20
|
+
ss = options[:subtitles].map { |s| self.subtitles[s] }
|
21
|
+
|
22
|
+
converter = Converter.new self, as, ss, options
|
23
|
+
converter.process
|
24
|
+
end
|
25
|
+
|
26
|
+
def convert_everything(options = {})
|
27
|
+
options[:audio] = self.audio.keys.sort
|
28
|
+
options[:subtitles] = self.subtitles.keys.sort
|
29
|
+
convert options
|
30
|
+
end
|
31
|
+
|
32
|
+
def print_description
|
33
|
+
print "Track: #{self.track_id}"
|
34
|
+
print " Duration: #{self.duration}" if self.duration
|
35
|
+
print "\n"
|
36
|
+
|
37
|
+
self.audio.keys.sort.each do |key|
|
38
|
+
a = self.audio[key]
|
39
|
+
a.print_description
|
40
|
+
end
|
41
|
+
|
42
|
+
self.subtitles.keys.sort.each do |key|
|
43
|
+
s = self.subtitles[key]
|
44
|
+
s.print_description
|
45
|
+
end
|
46
|
+
|
47
|
+
puts ''
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
metadata
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dvd-converter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 2
|
8
|
+
- 0
|
9
|
+
version: 0.2.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Stephen H. Gerstacker
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-12-24 00:00:00 -05:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: log4r
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 1
|
30
|
+
- 1
|
31
|
+
- 8
|
32
|
+
version: 1.1.8
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
description: " dvd-converter is a set of helper scripts to consistantly back up DVD's\n use good compression, choice of audio tracks and embedded (not burned in)\n subtitles.\n"
|
36
|
+
email: stephen@shortround.net
|
37
|
+
executables:
|
38
|
+
- dvd-converter
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files: []
|
42
|
+
|
43
|
+
files:
|
44
|
+
- lib/dvd_converter/audio.rb
|
45
|
+
- lib/dvd_converter/command_line.rb
|
46
|
+
- lib/dvd_converter/constants.rb
|
47
|
+
- lib/dvd_converter/converter.rb
|
48
|
+
- lib/dvd_converter/movie.rb
|
49
|
+
- lib/dvd_converter/requirements.rb
|
50
|
+
- lib/dvd_converter/subtitle.rb
|
51
|
+
- lib/dvd_converter/track.rb
|
52
|
+
- lib/dvd_converter.rb
|
53
|
+
- bin/dvd-converter
|
54
|
+
- dvd-converter.gemspec
|
55
|
+
- LICENSE
|
56
|
+
- README.markdown
|
57
|
+
has_rdoc: true
|
58
|
+
homepage: http://shortround.net/projects/dvd-converter
|
59
|
+
licenses: []
|
60
|
+
|
61
|
+
post_install_message:
|
62
|
+
rdoc_options: []
|
63
|
+
|
64
|
+
require_paths:
|
65
|
+
- lib
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
segments:
|
72
|
+
- 0
|
73
|
+
version: "0"
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
segments:
|
80
|
+
- 0
|
81
|
+
version: "0"
|
82
|
+
requirements: []
|
83
|
+
|
84
|
+
rubyforge_project:
|
85
|
+
rubygems_version: 1.3.7
|
86
|
+
signing_key:
|
87
|
+
specification_version: 3
|
88
|
+
summary: A set of helper scripts to back up DVD's
|
89
|
+
test_files: []
|
90
|
+
|