mkv 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/CHANGELOG +3 -0
- data/LICENSE +20 -0
- data/README.md +70 -0
- data/lib/mkv.rb +85 -0
- data/lib/mkv/error.rb +4 -0
- data/lib/mkv/io_patch.rb +59 -0
- data/lib/mkv/movie.rb +79 -0
- data/lib/mkv/track.rb +38 -0
- data/lib/mkv/version.rb +3 -0
- metadata +87 -0
data/CHANGELOG
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Pedro Rodrigues
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
MKV
|
2
|
+
===
|
3
|
+
|
4
|
+
Simple wrapper around MKVToolNix's mkvinfo utility to get data from MKV movies, and mkvextract to extract subtitles.
|
5
|
+
|
6
|
+
Installation
|
7
|
+
------------
|
8
|
+
|
9
|
+
(sudo) gem install mkv
|
10
|
+
|
11
|
+
This version is tested against MKVToolNix 5.8.0 build 2012-09-02-6920. So no guarantees with earlier versions.
|
12
|
+
|
13
|
+
Usage
|
14
|
+
-----
|
15
|
+
|
16
|
+
### Require the gem
|
17
|
+
|
18
|
+
``` ruby
|
19
|
+
require 'rubygems'
|
20
|
+
require 'mkv'
|
21
|
+
```
|
22
|
+
|
23
|
+
### Reading Metadata
|
24
|
+
|
25
|
+
``` ruby
|
26
|
+
movie = MKV::Movie.new("path/to/movie.mkv")
|
27
|
+
|
28
|
+
track = movie.tracks.first # Contains all streams
|
29
|
+
|
30
|
+
track.type # video, audio, or subtitles
|
31
|
+
track.uid
|
32
|
+
track.number
|
33
|
+
track.mkv_info_id
|
34
|
+
track.lacing
|
35
|
+
track.codec_id
|
36
|
+
|
37
|
+
# For video tracks
|
38
|
+
track.width
|
39
|
+
track.height
|
40
|
+
|
41
|
+
# For audio
|
42
|
+
track.sampling_frequency
|
43
|
+
track.channels
|
44
|
+
|
45
|
+
# For subtitle & audio tracks
|
46
|
+
track.language # ISO 639-3
|
47
|
+
track.enabled
|
48
|
+
track.default
|
49
|
+
track.forced
|
50
|
+
|
51
|
+
```
|
52
|
+
|
53
|
+
Specify the path to mkvinfo and mkvextract
|
54
|
+
--------------------------
|
55
|
+
|
56
|
+
By default, streamio assumes that the mkvinfo and mkvextract binaries are available in the default installation paths:
|
57
|
+
|
58
|
+
On Mac OSX:
|
59
|
+
``` ruby
|
60
|
+
MKV.mkvinfo_binary = "/Applications/Mkvtoolnix.app/Contents/MacOS/mkvinfo"
|
61
|
+
MKV.mkvextrack_binary = "/Applications/Mkvtoolnix.app/Contents/MacOS/mkvextract"
|
62
|
+
```
|
63
|
+
|
64
|
+
On Windows:
|
65
|
+
TODO!
|
66
|
+
|
67
|
+
Copyright
|
68
|
+
---------
|
69
|
+
|
70
|
+
Copyright (c) 2012 Pedro Rodrigues. See LICENSE for details.
|
data/lib/mkv.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
$LOAD_PATH.unshift File.dirname(__FILE__)
|
2
|
+
|
3
|
+
require 'logger'
|
4
|
+
require 'stringio'
|
5
|
+
require 'shellwords'
|
6
|
+
require 'open3'
|
7
|
+
require 'awesome_print'
|
8
|
+
|
9
|
+
require 'mkv/version'
|
10
|
+
require 'mkv/error'
|
11
|
+
require 'mkv/io_patch'
|
12
|
+
require 'mkv/movie'
|
13
|
+
require 'mkv/track'
|
14
|
+
|
15
|
+
module MKV
|
16
|
+
# MKV logs information about its progress when it's transcoding.
|
17
|
+
# Jack in your own logger through this method if you wish to.
|
18
|
+
#
|
19
|
+
# @param [Logger] log your own logger
|
20
|
+
# @return [Logger] the logger you set
|
21
|
+
def self.logger=(log)
|
22
|
+
@logger = log
|
23
|
+
end
|
24
|
+
|
25
|
+
# Get MKV logger.
|
26
|
+
#
|
27
|
+
# @return [Logger]
|
28
|
+
def self.logger
|
29
|
+
return @logger if @logger
|
30
|
+
logger = Logger.new(STDOUT)
|
31
|
+
logger.level = Logger::INFO
|
32
|
+
@logger = logger
|
33
|
+
end
|
34
|
+
|
35
|
+
# Set the path of the mkvinfo binary.
|
36
|
+
# Can be useful if you need to specify a path such as /usr/local/bin/MKV
|
37
|
+
#
|
38
|
+
# @param [String] path to the mkvinfo binary
|
39
|
+
# @return [String] the path you set
|
40
|
+
def self.mkvinfo_binary=(bin)
|
41
|
+
@mkvinfo_binary = bin
|
42
|
+
end
|
43
|
+
|
44
|
+
# Get the path to the mkvinfo binary, defaulting to an OS-dependent path
|
45
|
+
#
|
46
|
+
# @return [String] the path to the MKV binary
|
47
|
+
def self.mkvinfo_binary
|
48
|
+
@mkvinfo_binary.nil? ? default_mkvinfo_binary : @mkvinfo_binary
|
49
|
+
end
|
50
|
+
|
51
|
+
# Set the path of the mkvextract binary.
|
52
|
+
# Can be useful if you need to specify a path such as /usr/local/bin/MKV
|
53
|
+
#
|
54
|
+
# @param [String] path to the mkvextract binary
|
55
|
+
# @return [String] the path you set
|
56
|
+
def self.mkvextract_binary=(bin)
|
57
|
+
@mkvextract_binary = bin
|
58
|
+
end
|
59
|
+
|
60
|
+
# Get the path to the mkvextract binary, defaulting to an OS-dependent path
|
61
|
+
#
|
62
|
+
# @return [String] the path to the mkvextract binary
|
63
|
+
def self.mkvextract_binary
|
64
|
+
@mkvextract_binary.nil? ? default_mkvextract_binary : @mkvextract_binary
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def self.default_mkvinfo_binary
|
70
|
+
if is_macosx?
|
71
|
+
"/Applications/Mkvtoolnix.app/Contents/MacOS/mkvinfo"
|
72
|
+
else
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.default_mkvextract_binary
|
77
|
+
if is_macosx?
|
78
|
+
"/Applications/Mkvtoolnix.app/Contents/MacOS/mkvextract"
|
79
|
+
else
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.is_windows? ; RUBY_PLATFORM =~/.*?mingw.*?/i ; end
|
84
|
+
def self.is_macosx? ; RUBY_PLATFORM =~/.*?darwin.*?/i ; end
|
85
|
+
end
|
data/lib/mkv/error.rb
ADDED
data/lib/mkv/io_patch.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
if RUBY_VERSION =~ /1\.8/
|
2
|
+
# Useful when `timeout.rb`, which, on M.R.I 1.8, relies on green threads, does not work consistently.
|
3
|
+
begin
|
4
|
+
require 'system_timer'
|
5
|
+
MKV::Timer = SystemTimer
|
6
|
+
rescue LoadError
|
7
|
+
require 'timeout'
|
8
|
+
MKV::Timer = Timeout
|
9
|
+
end
|
10
|
+
else
|
11
|
+
require 'timeout'
|
12
|
+
MKV::Timer = Timeout
|
13
|
+
end
|
14
|
+
|
15
|
+
require 'win32/process' if RUBY_PLATFORM =~ /(win|w)(32|64)$/
|
16
|
+
|
17
|
+
#
|
18
|
+
# Monkey Patch timeout support into the IO class
|
19
|
+
#
|
20
|
+
class IO
|
21
|
+
def each_with_timeout(pid, seconds, sep_string=$/)
|
22
|
+
sleeping_queue = Queue.new
|
23
|
+
thread = nil
|
24
|
+
|
25
|
+
timer_set = lambda do
|
26
|
+
thread = new_thread(pid) { MKV::Timer.timeout(seconds) { sleeping_queue.pop } }
|
27
|
+
end
|
28
|
+
|
29
|
+
timer_cancel = lambda do
|
30
|
+
thread.kill if thread rescue nil
|
31
|
+
end
|
32
|
+
|
33
|
+
timer_set.call
|
34
|
+
each(sep_string) do |buffer|
|
35
|
+
timer_cancel.call
|
36
|
+
yield buffer
|
37
|
+
timer_set.call
|
38
|
+
end
|
39
|
+
ensure
|
40
|
+
timer_cancel.call
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
def new_thread(pid, &block)
|
45
|
+
current_thread = Thread.current
|
46
|
+
Thread.new do
|
47
|
+
begin
|
48
|
+
block.call
|
49
|
+
rescue Exception => e
|
50
|
+
current_thread.raise e
|
51
|
+
if RUBY_PLATFORM =~ /(win|w)(32|64)$/
|
52
|
+
Process.kill(1, pid)
|
53
|
+
else
|
54
|
+
Process.kill('SIGKILL', pid)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/mkv/movie.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'time'
|
2
|
+
|
3
|
+
module MKV
|
4
|
+
class Movie
|
5
|
+
@@timeout = 200
|
6
|
+
|
7
|
+
attr_reader :path
|
8
|
+
attr_reader :tracks
|
9
|
+
|
10
|
+
def initialize(path)
|
11
|
+
raise Errno::ENOENT, "the file '#{path}' does not exist" unless File.exists?(path)
|
12
|
+
|
13
|
+
@path = path
|
14
|
+
|
15
|
+
# mkvinfo will output to stdout
|
16
|
+
command = "#{MKV.mkvinfo_binary} #{Shellwords.escape(path)}"
|
17
|
+
MKV.logger.info(command)
|
18
|
+
output = Open3.popen3(command) { |stdin, stdout, stderr| stdout.read}
|
19
|
+
|
20
|
+
match = output.gsub(/\n/, '$$').match /\|\+\ssegment tracks(.*?)\|\+\s(?:chapters|cluster)/i
|
21
|
+
tracks = match[1].gsub(/\$\$/, "\n")
|
22
|
+
match_tracks = tracks.gsub(/\n/, '$$').scan(/a track(.*?)(?:\|\s\+|$)/i)
|
23
|
+
match_tracks = match_tracks.map { |x| x.first.gsub(/\$\$/, "\n") }
|
24
|
+
|
25
|
+
@tracks = match_tracks.map do |track_data|
|
26
|
+
MKV::Track.new track_data
|
27
|
+
end
|
28
|
+
|
29
|
+
@invalid = true if @tracks.any?
|
30
|
+
@invalid = true if output.include?("is not supported")
|
31
|
+
@invalid = true if output.include?("could not find codec parameters")
|
32
|
+
end
|
33
|
+
|
34
|
+
def valid?
|
35
|
+
not @invalid
|
36
|
+
end
|
37
|
+
|
38
|
+
def has_subtitles? ; tracks.select { |t| t.type == 'subtitles' }.any? ; end
|
39
|
+
def has_video? ; tracks.select { |t| t.type == 'video' }.any? ; end
|
40
|
+
def has_audio? ; tracks.select { |t| t.type == 'audio' }.any? ; end
|
41
|
+
|
42
|
+
def extract_subtitles(destination_dir)
|
43
|
+
tracks.select { |t| t.type == 'subtitles' }.each do |track|
|
44
|
+
destination_filename = File.basename(@path).gsub(/\.mkv$/i, %Q[.#{track.language}.srt])
|
45
|
+
command = %Q[#{MKV.mkvextract_binary} tracks "#{@path}" #{track.mkv_info_id}:"#{File.join(destination_dir, destination_filename)}"]
|
46
|
+
|
47
|
+
output = ""
|
48
|
+
Open3.popen3(command) do |stdin, stdout, stderr, wait_thr|
|
49
|
+
begin
|
50
|
+
yield(0.0) if block_given?
|
51
|
+
next_line = Proc.new do |line|
|
52
|
+
output << line
|
53
|
+
if line =~ /(\d+)%/
|
54
|
+
progress = $1.to_i
|
55
|
+
|
56
|
+
yield(progress) if block_given?
|
57
|
+
end
|
58
|
+
|
59
|
+
if line =~ /Unsupported codec/
|
60
|
+
MKV.logger.error "Failed encoding...\nCommand\n#{command}\nOutput\n#{output}\n"
|
61
|
+
raise "Failed encoding: #{line}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
if @@timeout
|
66
|
+
stdout.each_with_timeout(wait_thr.pid, @@timeout, "r", &next_line)
|
67
|
+
else
|
68
|
+
stdout.each("r", &next_line)
|
69
|
+
end
|
70
|
+
|
71
|
+
rescue Timeout::Error => e
|
72
|
+
MKV.logger.error "Process hung...\nCommand\n#{command}\nOutput\n#{output}\n"
|
73
|
+
raise MKV::Error, "Process hung. Full output: #{output}"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/lib/mkv/track.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
module MKV
|
2
|
+
class Track
|
3
|
+
# Video, Audio & Subtitle
|
4
|
+
attr_reader :type, :uid, :number, :mkv_info_id, :lacing, :codec_id
|
5
|
+
# Video
|
6
|
+
attr_reader :width, :height
|
7
|
+
# Audio
|
8
|
+
attr_reader :sampling_frequency, :channels
|
9
|
+
# Subtitle & Audio
|
10
|
+
attr_reader :language, :enabled, :default, :forced
|
11
|
+
|
12
|
+
def initialize(data)
|
13
|
+
(@number, @mkv_info_id) = data.match(/track number:\s(\d+)\s\(track ID for mkvmerge & mkvextract: (\d+)\)/i)[1..2]
|
14
|
+
@uid = data.match(/track uid: (\d+)/i)[1]
|
15
|
+
@lacing = data.match(/lacing flag: (\d+)/i)[1] != '0'
|
16
|
+
@type = data.match(/track type: (\w+)/i)[1]
|
17
|
+
@codec_id = data.match(/codec id: (.*)/i)[1]
|
18
|
+
|
19
|
+
if @type == 'video'
|
20
|
+
@width = data.match(/pixel width: (\d+)/i)[1].to_i
|
21
|
+
@height = data.match(/pixel height: (\d+)/i)[1].to_i
|
22
|
+
end
|
23
|
+
|
24
|
+
if @type == 'audio'
|
25
|
+
@sampling_frequency = data.match(/sampling frequency: (\d+)/i)[1].to_i
|
26
|
+
@channels = data.match(/channels: (\d+)/i)[1].to_i
|
27
|
+
end
|
28
|
+
|
29
|
+
if @type == 'audio' || @type == 'subtitles'
|
30
|
+
@language = data.match(/language: (\w+)/i)[1]
|
31
|
+
@enabled = data.match(/enabled: (\d+)/i)[1] != '0'
|
32
|
+
@default = data.match(/default flag: (\d+)/i)[1] != '0'
|
33
|
+
@forced = data.match(/forced flag: (\d+)/i)[1] != '0'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
data/lib/mkv/version.rb
ADDED
metadata
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mkv
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Pedro Rodrigues
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-11-15 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '2.7'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '2.7'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 0.9.2
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 0.9.2
|
46
|
+
description: Simple wrapper around MKVToolNix's mkvinfo utility to get data from MKV
|
47
|
+
movies, and mkvextract to extract subtitles.
|
48
|
+
email:
|
49
|
+
- pedro@bbde.org
|
50
|
+
executables: []
|
51
|
+
extensions: []
|
52
|
+
extra_rdoc_files: []
|
53
|
+
files:
|
54
|
+
- lib/mkv/error.rb
|
55
|
+
- lib/mkv/io_patch.rb
|
56
|
+
- lib/mkv/movie.rb
|
57
|
+
- lib/mkv/track.rb
|
58
|
+
- lib/mkv/version.rb
|
59
|
+
- lib/mkv.rb
|
60
|
+
- README.md
|
61
|
+
- LICENSE
|
62
|
+
- CHANGELOG
|
63
|
+
homepage: http://github.com/gokuu/mkv
|
64
|
+
licenses: []
|
65
|
+
post_install_message:
|
66
|
+
rdoc_options: []
|
67
|
+
require_paths:
|
68
|
+
- lib
|
69
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
71
|
+
requirements:
|
72
|
+
- - ! '>='
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
|
+
none: false
|
77
|
+
requirements:
|
78
|
+
- - ! '>='
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0'
|
81
|
+
requirements: []
|
82
|
+
rubyforge_project:
|
83
|
+
rubygems_version: 1.8.24
|
84
|
+
signing_key:
|
85
|
+
specification_version: 3
|
86
|
+
summary: Reads MKV info.
|
87
|
+
test_files: []
|