mkv 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG ADDED
@@ -0,0 +1,3 @@
1
+ v0.0.1
2
+ ------
3
+ * Mac OSX implementation
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
@@ -0,0 +1,4 @@
1
+ module MKV
2
+ class Error < StandardError
3
+ end
4
+ end
@@ -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
@@ -0,0 +1,3 @@
1
+ module MKV
2
+ VERSION = "0.0.1"
3
+ end
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: []