smart_ass 0.1.0
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 +17 -0
- data/.rspec +2 -0
- data/Gemfile +6 -0
- data/LICENSE +22 -0
- data/README.md +51 -0
- data/Rakefile +7 -0
- data/bin/smart_ass +13 -0
- data/lib/smart_ass/app.rb +67 -0
- data/lib/smart_ass/ass.rb +40 -0
- data/lib/smart_ass/bt601_colormatrix.rb +5 -0
- data/lib/smart_ass/bt709_colormatrix.rb +9 -0
- data/lib/smart_ass/rgba_color.rb +49 -0
- data/lib/smart_ass/version.rb +3 -0
- data/lib/smart_ass/ycbcr_colormatrix.rb +96 -0
- data/lib/smart_ass/ycbcr_converter.rb +15 -0
- data/lib/smart_ass.rb +11 -0
- data/smart_ass.gemspec +17 -0
- data/spec/ass_spec.rb +29 -0
- data/spec/fixtures/info_0 +15 -0
- data/spec/fixtures/info_1_t_3 +16 -0
- data/spec/fixtures/info_3_t_3_5_6 +19 -0
- data/spec/rgba_color_spec.rb +43 -0
- data/spec/spec_helper.rb +11 -0
- data/spec/ycbcr_converter_spec.rb +15 -0
- metadata +77 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Stefano Pigozzi
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# SmartAss
|
2
|
+
|
3
|
+
Utility for Y'CbCr ColorMatrix conversion of ASS Subtitles
|
4
|
+
|
5
|
+
If you are smart and know about ASS subtitles you are probably wondering
|
6
|
+
what Y'CbCr has to do with ASS. As a matter of fact ASS uses RGB hex for
|
7
|
+
color representation.
|
8
|
+
|
9
|
+
The problem is some subtitles renderers (VSFilter) on Windows decided it was
|
10
|
+
too stupid to render the RGB directly (don't even ask about it). So they
|
11
|
+
started doing a `R'G'B' ->(BT.601) Y'CbCr ->(BT.601) R'G'B'` chain of
|
12
|
+
conversions.
|
13
|
+
|
14
|
+
The problem is some fansubbers started to actually author content for
|
15
|
+
these broken renderers and the result is you get broken colors on
|
16
|
+
subtitles in the video players that are actually getting it right.
|
17
|
+
|
18
|
+
This tool is an attempt to fix this problem. Using mkvextract/mkvmerge
|
19
|
+
and some matrix multiplications it generates a new ass file where the
|
20
|
+
colors passed through the following conversions:
|
21
|
+
|
22
|
+
R'G'B' ->(BT.601) Y'CbCr ->(BT.709) R'G'B'
|
23
|
+
|
24
|
+
## Installation
|
25
|
+
|
26
|
+
Make sure you have mkvtoolnix (mkvmerge and mkvextract) utilities
|
27
|
+
installed and in your path. For example, on OSX and homebrew it's as simple as:
|
28
|
+
|
29
|
+
brew install mkvtoolnix
|
30
|
+
|
31
|
+
Install the Ruby Gem:
|
32
|
+
|
33
|
+
[sudo] gem smart_ass
|
34
|
+
|
35
|
+
## Usage
|
36
|
+
|
37
|
+
This is really minimal. No options for now.
|
38
|
+
|
39
|
+
smart_ass path/to/movie.mkv
|
40
|
+
|
41
|
+
## Contributing
|
42
|
+
|
43
|
+
1. Fork it
|
44
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
45
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
46
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
47
|
+
5. Create new Pull Request
|
48
|
+
|
49
|
+
## Contacts
|
50
|
+
|
51
|
+
Look for pigoz on irc.freenode.net.
|
data/Rakefile
ADDED
data/bin/smart_ass
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# vim: syntax=ruby
|
3
|
+
$:.unshift File.join(File.dirname(File.dirname(__FILE__)), 'lib')
|
4
|
+
|
5
|
+
require 'smart_ass'
|
6
|
+
|
7
|
+
if arg = ARGV[0] then
|
8
|
+
app = SmartAss::App.new(arg)
|
9
|
+
app.run
|
10
|
+
else
|
11
|
+
puts "Please provide an input file"
|
12
|
+
exit()
|
13
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
class SmartAss::App
|
2
|
+
def initialize(file)
|
3
|
+
@ass = SmartAss::Ass.new(file)
|
4
|
+
@file = Pathname.new(file)
|
5
|
+
end
|
6
|
+
|
7
|
+
def run
|
8
|
+
# function composition baby!
|
9
|
+
write_ass_script replace_colors extract track
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
def write_ass_script(ass_script)
|
14
|
+
script_file_basename = "#{@file.basename(@file.extname)}.ass"
|
15
|
+
File.open(@file.expand_path.dirname + script_file_basename, "w") {|f|
|
16
|
+
f.puts(ass_script)
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
def replace_colors(ass_script)
|
21
|
+
ass_script.gsub(/&H[\da-zA-Z]{6,8}/) {|c| convert_color(c)}
|
22
|
+
end
|
23
|
+
|
24
|
+
def convert_color(c)
|
25
|
+
@_colors_cache ||= Hash.new
|
26
|
+
|
27
|
+
unless @_colors_cache[c]
|
28
|
+
color = SmartAss::RGBAColor.from_ass(c)
|
29
|
+
|
30
|
+
bt601 = SmartAss::YCbCrConverter.new(:bt601)
|
31
|
+
bt709 = SmartAss::YCbCrConverter.new(:bt709)
|
32
|
+
|
33
|
+
ycbcr = bt601.to_ycbcr(*color.rgb_components)
|
34
|
+
rgb = bt709.to_rgb(*ycbcr)
|
35
|
+
|
36
|
+
puts "[converted color] ass-hex: #{c}, rgb: #{color.rgb_components}, " \
|
37
|
+
"ycbcr: #{ycbcr}, mangled-rgb: #{rgb}"
|
38
|
+
|
39
|
+
@_colors_cache[c] = SmartAss::RGBAColor.from_rgba(*rgb).to_ass
|
40
|
+
end
|
41
|
+
|
42
|
+
@_colors_cache[c]
|
43
|
+
end
|
44
|
+
|
45
|
+
def track
|
46
|
+
if @ass.tracks.size > 1
|
47
|
+
track = ask_for_track
|
48
|
+
elsif @ass.tracks.size == 1
|
49
|
+
track = @ass.tracks[0]
|
50
|
+
else
|
51
|
+
puts "No ASS tracks found. Aborting."
|
52
|
+
exit
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def ask_for_track
|
57
|
+
puts "Please enter a track name - #{@ass.tracks.inspect}:"
|
58
|
+
readline.to_i
|
59
|
+
rescue
|
60
|
+
puts "Invalid track provided. Try again."
|
61
|
+
retry
|
62
|
+
end
|
63
|
+
|
64
|
+
def extract(track_id)
|
65
|
+
@ass.extract(track_id)
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
class SmartAss::Ass
|
4
|
+
attr_reader :file
|
5
|
+
|
6
|
+
def initialize(file)
|
7
|
+
@file = Pathname.new(file)
|
8
|
+
end
|
9
|
+
|
10
|
+
def tracks
|
11
|
+
identify.each_line \
|
12
|
+
.map {|l| (l =~ identify_ass_regexp) ? $1 : nil } \
|
13
|
+
.compact \
|
14
|
+
.map &:to_i
|
15
|
+
end
|
16
|
+
|
17
|
+
# TODO: test this!
|
18
|
+
def extract(track_id)
|
19
|
+
puts `mkvextract tracks "#{expanded_file}" #{track_id}:#{export_temp_file}`
|
20
|
+
IO.read(export_temp_file, :encoding => 'UTF-8') \
|
21
|
+
.tap {|_| File.delete(export_temp_file) }
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
def identify
|
26
|
+
`mkvmerge -i "#{expanded_file}"`
|
27
|
+
end
|
28
|
+
|
29
|
+
def expanded_file
|
30
|
+
file.expand_path
|
31
|
+
end
|
32
|
+
|
33
|
+
def identify_ass_regexp
|
34
|
+
/Track ID (\d): subtitles \(S_TEXT\/ASS\)/
|
35
|
+
end
|
36
|
+
|
37
|
+
def export_temp_file
|
38
|
+
"/tmp/smart_ass_export-#{Process.pid}"
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
class SmartAss::RGBAColor
|
2
|
+
attr_reader :r, :g, :b, :a
|
3
|
+
|
4
|
+
def self.from_ass(string)
|
5
|
+
argb_hex = string.gsub(/^&H/, '')
|
6
|
+
components = argb_hex.scan(/.{2}/).map {|h| h.to_i(16)}
|
7
|
+
if components.size == 4
|
8
|
+
from_argb(*components)
|
9
|
+
else
|
10
|
+
from_rgba(*components)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.from_argb(*components)
|
15
|
+
from_rgba(*components.push(components.shift))
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.from_rgba(*components)
|
19
|
+
new(*components)
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_ass
|
23
|
+
hex = argb_components
|
24
|
+
.map {|c| c.to_s(16)}
|
25
|
+
.map {|c| c.rjust(2, "0")}
|
26
|
+
.map(&:upcase)
|
27
|
+
.join
|
28
|
+
"&H#{hex}"
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize(r, g, b, a=0x00)
|
32
|
+
@r, @g, @b, @a = [r, g, b, a].map {|n| n < 0 ? 0 : n}
|
33
|
+
end
|
34
|
+
|
35
|
+
def components
|
36
|
+
[@r, @g, @b, @a]
|
37
|
+
end
|
38
|
+
|
39
|
+
def argb_components
|
40
|
+
c = components
|
41
|
+
c.unshift(c.pop)
|
42
|
+
end
|
43
|
+
|
44
|
+
def rgb_components
|
45
|
+
c = components
|
46
|
+
c.pop
|
47
|
+
c
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'matrix'
|
2
|
+
|
3
|
+
class SmartAss::YCbCrColorMatrix
|
4
|
+
attr_reader :wr, :wg, :wb
|
5
|
+
def initialize(wr, wg, wb)
|
6
|
+
@wr, @wg, @wb = wr, wg, wb
|
7
|
+
end
|
8
|
+
|
9
|
+
# basic luma relation: converts R'G'B' -> Y'(B'-Y')(R'-Y')
|
10
|
+
def to_ybmyrmy_matrix
|
11
|
+
Matrix[
|
12
|
+
[ wr, wg, wb ],
|
13
|
+
[-wr, -wg, wr+wg],
|
14
|
+
[wg+wb, -wg, -wb ],
|
15
|
+
]
|
16
|
+
end
|
17
|
+
|
18
|
+
# RGB -> YPbPr []
|
19
|
+
def to_ypbpr_matrix_from_rgb
|
20
|
+
scale_rows to_ybmyrmy_matrix, 1.0, 0.5 / (wr+wg), 0.5 / (wg+wb)
|
21
|
+
end
|
22
|
+
|
23
|
+
# YPbPr -> RGB []
|
24
|
+
def to_rgb_matrix_from_ypbpr
|
25
|
+
zeros to_ypbpr_matrix_from_rgb.inverse
|
26
|
+
end
|
27
|
+
|
28
|
+
# RGB -> YCbCr []
|
29
|
+
def to_ycbcr_matrix_from_rgb
|
30
|
+
scale_rows to_ypbpr_matrix_from_rgb, 219.0, 224.0, 224.0
|
31
|
+
end
|
32
|
+
|
33
|
+
# YCbCr -> RGB []
|
34
|
+
def to_rgb_matrix_from_ycbcr
|
35
|
+
zeros to_ycbcr_matrix_from_rgb.inverse
|
36
|
+
end
|
37
|
+
|
38
|
+
# RGB(digital) -> YCbCr []
|
39
|
+
def to_ycbcr_matrix_from_rgbd
|
40
|
+
scale_rows to_ycbcr_matrix_from_rgb, *[256.0/255.0]*3
|
41
|
+
end
|
42
|
+
|
43
|
+
# YCbCr -> RGB(digital) []
|
44
|
+
def to_rgbd_matrix_from_ycbcr
|
45
|
+
scale_rows zeros(to_ycbcr_matrix_from_rgbd.inverse), *[256.0*256.0]*3
|
46
|
+
end
|
47
|
+
|
48
|
+
# RGB(digital) -> YCbCr Equation
|
49
|
+
def to_ycbcr_from_rgb(*rgb)
|
50
|
+
apply_equation(*rgb) do |*rgb|
|
51
|
+
offset_vector + (to_ycbcr_matrix_from_rgbd * 1.0 / 256.0) *
|
52
|
+
Matrix.column_vector(rgb)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# YCbCr -> RGB(digital) Equation
|
57
|
+
def to_rgb_from_ycbcr(*ycbcr)
|
58
|
+
r = apply_equation(*ycbcr) do |*ycbcr|
|
59
|
+
(to_rgbd_matrix_from_ycbcr * 1.0 / 256.0) *
|
60
|
+
(Matrix.column_vector(ycbcr) - offset_vector)
|
61
|
+
end.map(&:round)
|
62
|
+
|
63
|
+
clip_rgb(r)
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
def clip_rgb(input)
|
68
|
+
input.map {|n| n >= 0 ? n : 0}
|
69
|
+
.map {|n| n <= 255 ? n : 255}
|
70
|
+
end
|
71
|
+
|
72
|
+
def offset_vector
|
73
|
+
Matrix.column_vector([16.0, 128.0, 128.0])
|
74
|
+
end
|
75
|
+
|
76
|
+
def apply_equation(*inputs, &block)
|
77
|
+
block.call(*inputs).to_a.flatten
|
78
|
+
end
|
79
|
+
|
80
|
+
def scale_rows(matrix, *factors)
|
81
|
+
raise InvalidArgumentError unless matrix.row_size == factors.size
|
82
|
+
Matrix[*factors.each_with_index.map{|factor, index|
|
83
|
+
matrix.row(index) * factor
|
84
|
+
}]
|
85
|
+
end
|
86
|
+
|
87
|
+
def zeros(matrix)
|
88
|
+
matrix.map {|n|
|
89
|
+
if n < 1e-5 and n > -1e-5 then
|
90
|
+
0.0
|
91
|
+
else
|
92
|
+
n
|
93
|
+
end
|
94
|
+
}
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class SmartAss::YCbCrConverter
|
2
|
+
attr_reader :matrix
|
3
|
+
|
4
|
+
def initialize(matrix)
|
5
|
+
@matrix = SmartAss.const_get("#{matrix.upcase}ColorMatrix".to_sym).new
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_ycbcr(*rgb)
|
9
|
+
matrix.to_ycbcr_from_rgb(*rgb)
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_rgb(*ycbcr)
|
13
|
+
matrix.to_rgb_from_ycbcr(*ycbcr)
|
14
|
+
end
|
15
|
+
end
|
data/lib/smart_ass.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require "smart_ass/version"
|
2
|
+
|
3
|
+
module SmartAss
|
4
|
+
autoload :App, 'smart_ass/app'
|
5
|
+
autoload :Ass, 'smart_ass/ass'
|
6
|
+
autoload :BT601ColorMatrix, 'smart_ass/bt601_colormatrix'
|
7
|
+
autoload :BT709ColorMatrix, 'smart_ass/bt709_colormatrix'
|
8
|
+
autoload :RGBAColor, 'smart_ass/rgba_color'
|
9
|
+
autoload :YCbCrColorMatrix, 'smart_ass/ycbcr_colormatrix'
|
10
|
+
autoload :YCbCrConverter, 'smart_ass/ycbcr_converter'
|
11
|
+
end
|
data/smart_ass.gemspec
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/smart_ass/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Stefano Pigozzi"]
|
6
|
+
gem.email = ["stefano.pigozzi@gmail.com"]
|
7
|
+
gem.description = %q{Utility for YCbCr ColorMatrix conversion of ASS Subtitles.}
|
8
|
+
gem.summary = %q{Utility for YCbCr ColorMatrix conversion of ASS Subtitles.}
|
9
|
+
gem.homepage = "http://github.com/pigoz/smart_ass"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "smart_ass"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = SmartAss::VERSION
|
17
|
+
end
|
data/spec/ass_spec.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe SmartAss::Ass do
|
4
|
+
subject {
|
5
|
+
@file = "path/to/file/movie.mkv"
|
6
|
+
SmartAss::Ass.new(@file)
|
7
|
+
}
|
8
|
+
|
9
|
+
it "holds onto a file" do
|
10
|
+
subject.file.should == Pathname.new(@file)
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "#tracks" do
|
14
|
+
[
|
15
|
+
{:f => 'info_1_t_3', :e => [3], :t => 'with 1 element'},
|
16
|
+
{:f => 'info_3_t_3_5_6', :e => [3, 5, 6], :t => 'with many elements'},
|
17
|
+
{:f => 'info_0', :e => [], :t => 'with 0 elements'},
|
18
|
+
].each do |test|
|
19
|
+
it "returns an array of ASS track IDs (#{test[:t]})" do
|
20
|
+
mock(subject).identify {
|
21
|
+
IO.read(
|
22
|
+
File.join(File.dirname(__FILE__), 'fixtures', test[:f]))
|
23
|
+
}
|
24
|
+
subject.tracks.should == test[:e]
|
25
|
+
end # it #tracks
|
26
|
+
end # do
|
27
|
+
end # describe
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
File '/Users/stefano/Movies/[UTWoots]_Sword_Art_Online_-_01v2_[720p][E22288BC].mkv': container: Matroska
|
2
|
+
Track ID 1: video (V_MPEG4/ISO/AVC)
|
3
|
+
Track ID 2: audio (A_AAC)
|
4
|
+
Attachment ID 1: type 'application/x-truetype-font', size 168852 bytes, file name 'ACaslonPro-Regular.otf'
|
5
|
+
Attachment ID 2: type 'application/x-truetype-font', size 129676 bytes, file name 'GOTHICB.TTF'
|
6
|
+
Attachment ID 3: type 'application/x-truetype-font', size 6455918 bytes, file name 'IwaOMinPro-Hv-En.ttf'
|
7
|
+
Attachment ID 4: type 'application/x-truetype-font', size 767840 bytes, file name 'LinLibertine_DR.ttf'
|
8
|
+
Attachment ID 5: type 'application/x-truetype-font', size 43052 bytes, file name 'Montara-Bold.otf'
|
9
|
+
Attachment ID 6: type 'application/x-truetype-font', size 42004 bytes, file name 'Montara-BoldItalic.otf'
|
10
|
+
Attachment ID 7: type 'application/x-truetype-font', size 45316 bytes, file name 'Montara-Gothic.otf'
|
11
|
+
Attachment ID 8: type 'application/x-truetype-font', size 42608 bytes, file name 'Montara-Italic.TTF'
|
12
|
+
Attachment ID 9: type 'application/x-truetype-font', size 53092 bytes, file name 'QuaySansMdITCTT.ttf'
|
13
|
+
Attachment ID 10: type 'application/x-truetype-font', size 94300 bytes, file name 'raavi.ttf'
|
14
|
+
Attachment ID 11: type 'application/x-truetype-font', size 30352 bytes, file name 'wscsnrg.TTF'
|
15
|
+
Chapters: 6 entries
|
@@ -0,0 +1,16 @@
|
|
1
|
+
File '/Users/stefano/Movies/[UTWoots]_Sword_Art_Online_-_01v2_[720p][E22288BC].mkv': container: Matroska
|
2
|
+
Track ID 1: video (V_MPEG4/ISO/AVC)
|
3
|
+
Track ID 2: audio (A_AAC)
|
4
|
+
Track ID 3: subtitles (S_TEXT/ASS)
|
5
|
+
Attachment ID 1: type 'application/x-truetype-font', size 168852 bytes, file name 'ACaslonPro-Regular.otf'
|
6
|
+
Attachment ID 2: type 'application/x-truetype-font', size 129676 bytes, file name 'GOTHICB.TTF'
|
7
|
+
Attachment ID 3: type 'application/x-truetype-font', size 6455918 bytes, file name 'IwaOMinPro-Hv-En.ttf'
|
8
|
+
Attachment ID 4: type 'application/x-truetype-font', size 767840 bytes, file name 'LinLibertine_DR.ttf'
|
9
|
+
Attachment ID 5: type 'application/x-truetype-font', size 43052 bytes, file name 'Montara-Bold.otf'
|
10
|
+
Attachment ID 6: type 'application/x-truetype-font', size 42004 bytes, file name 'Montara-BoldItalic.otf'
|
11
|
+
Attachment ID 7: type 'application/x-truetype-font', size 45316 bytes, file name 'Montara-Gothic.otf'
|
12
|
+
Attachment ID 8: type 'application/x-truetype-font', size 42608 bytes, file name 'Montara-Italic.TTF'
|
13
|
+
Attachment ID 9: type 'application/x-truetype-font', size 53092 bytes, file name 'QuaySansMdITCTT.ttf'
|
14
|
+
Attachment ID 10: type 'application/x-truetype-font', size 94300 bytes, file name 'raavi.ttf'
|
15
|
+
Attachment ID 11: type 'application/x-truetype-font', size 30352 bytes, file name 'wscsnrg.TTF'
|
16
|
+
Chapters: 6 entries
|
@@ -0,0 +1,19 @@
|
|
1
|
+
File '/Users/stefano/Movies/[UTWoots]_Sword_Art_Online_-_01v2_[720p][E22288BC].mkv': container: Matroska
|
2
|
+
Track ID 1: video (V_MPEG4/ISO/AVC)
|
3
|
+
Track ID 2: audio (A_AAC)
|
4
|
+
Track ID 3: subtitles (S_TEXT/ASS)
|
5
|
+
Track ID 4: audio (A_AAC)
|
6
|
+
Track ID 5: subtitles (S_TEXT/ASS)
|
7
|
+
Track ID 6: subtitles (S_TEXT/ASS)
|
8
|
+
Attachment ID 1: type 'application/x-truetype-font', size 168852 bytes, file name 'ACaslonPro-Regular.otf'
|
9
|
+
Attachment ID 2: type 'application/x-truetype-font', size 129676 bytes, file name 'GOTHICB.TTF'
|
10
|
+
Attachment ID 3: type 'application/x-truetype-font', size 6455918 bytes, file name 'IwaOMinPro-Hv-En.ttf'
|
11
|
+
Attachment ID 4: type 'application/x-truetype-font', size 767840 bytes, file name 'LinLibertine_DR.ttf'
|
12
|
+
Attachment ID 5: type 'application/x-truetype-font', size 43052 bytes, file name 'Montara-Bold.otf'
|
13
|
+
Attachment ID 6: type 'application/x-truetype-font', size 42004 bytes, file name 'Montara-BoldItalic.otf'
|
14
|
+
Attachment ID 7: type 'application/x-truetype-font', size 45316 bytes, file name 'Montara-Gothic.otf'
|
15
|
+
Attachment ID 8: type 'application/x-truetype-font', size 42608 bytes, file name 'Montara-Italic.TTF'
|
16
|
+
Attachment ID 9: type 'application/x-truetype-font', size 53092 bytes, file name 'QuaySansMdITCTT.ttf'
|
17
|
+
Attachment ID 10: type 'application/x-truetype-font', size 94300 bytes, file name 'raavi.ttf'
|
18
|
+
Attachment ID 11: type 'application/x-truetype-font', size 30352 bytes, file name 'wscsnrg.TTF'
|
19
|
+
Chapters: 6 entries
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require "spec_helper.rb"
|
2
|
+
|
3
|
+
describe SmartAss::RGBAColor do
|
4
|
+
|
5
|
+
it "holds onto its components" do
|
6
|
+
c = SmartAss::RGBAColor.new(50, 150, 200, 255)
|
7
|
+
c.r.should == 50
|
8
|
+
c.g.should == 150
|
9
|
+
c.b.should == 200
|
10
|
+
c.a.should == 255
|
11
|
+
c.components.should == [50, 150, 200, 255]
|
12
|
+
end
|
13
|
+
|
14
|
+
it "can be created from a hex rgba" do
|
15
|
+
c = SmartAss::RGBAColor.from_rgba(0xff, 0xff, 0xff, 0x00)
|
16
|
+
c.components.should == [255, 255, 255, 0]
|
17
|
+
end
|
18
|
+
|
19
|
+
it "can be created from a hex argb" do
|
20
|
+
c = SmartAss::RGBAColor.from_argb(0x00, 0xff, 0xff, 0xff)
|
21
|
+
c.components.should == [255, 255, 255, 0]
|
22
|
+
end
|
23
|
+
|
24
|
+
it "can be created from an .ass hex (with alpha)" do
|
25
|
+
c = SmartAss::RGBAColor.from_ass("&H00FFFFFF")
|
26
|
+
c.components.should == [255, 255, 255, 0]
|
27
|
+
end
|
28
|
+
|
29
|
+
it "can be created from an .ass hex (without alpha)" do
|
30
|
+
c = SmartAss::RGBAColor.from_ass("&HFFFFFF")
|
31
|
+
c.components.should == [255, 255, 255, 0]
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "#to_ass" do
|
35
|
+
it "returns ass repesentation" do
|
36
|
+
c = SmartAss::RGBAColor.from_argb(0x00, 0xff, 0xff, 0xff)
|
37
|
+
c.to_ass.should == "&H00FFFFFF"
|
38
|
+
|
39
|
+
c = SmartAss::RGBAColor.from_rgba(*[0xff, 0xff, 0xff] + [0x00])
|
40
|
+
c.to_ass.should == "&H00FFFFFF"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
Bundler.require(:default)
|
4
|
+
|
5
|
+
RSpec.configure do |config|
|
6
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
7
|
+
config.run_all_when_everything_filtered = true
|
8
|
+
config.filter_run :focus
|
9
|
+
config.order = 'random'
|
10
|
+
config.mock_with :rr
|
11
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe SmartAss::YCbCrConverter do
|
4
|
+
|
5
|
+
it "can mangle RGB doing RGB ->(bt601) YCbCr ->(b709) RGB" do
|
6
|
+
bt601 = SmartAss::YCbCrConverter.new(:bt601)
|
7
|
+
bt709 = SmartAss::YCbCrConverter.new(:bt709)
|
8
|
+
|
9
|
+
yuv = bt601.to_ycbcr(16, 138, 187)
|
10
|
+
rgb = bt709.to_rgb(*yuv)
|
11
|
+
|
12
|
+
rgb.should =~ [5, 129, 191]
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
metadata
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: smart_ass
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Stefano Pigozzi
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-07-17 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: Utility for YCbCr ColorMatrix conversion of ASS Subtitles.
|
15
|
+
email:
|
16
|
+
- stefano.pigozzi@gmail.com
|
17
|
+
executables:
|
18
|
+
- smart_ass
|
19
|
+
extensions: []
|
20
|
+
extra_rdoc_files: []
|
21
|
+
files:
|
22
|
+
- .gitignore
|
23
|
+
- .rspec
|
24
|
+
- Gemfile
|
25
|
+
- LICENSE
|
26
|
+
- README.md
|
27
|
+
- Rakefile
|
28
|
+
- bin/smart_ass
|
29
|
+
- lib/smart_ass.rb
|
30
|
+
- lib/smart_ass/app.rb
|
31
|
+
- lib/smart_ass/ass.rb
|
32
|
+
- lib/smart_ass/bt601_colormatrix.rb
|
33
|
+
- lib/smart_ass/bt709_colormatrix.rb
|
34
|
+
- lib/smart_ass/rgba_color.rb
|
35
|
+
- lib/smart_ass/version.rb
|
36
|
+
- lib/smart_ass/ycbcr_colormatrix.rb
|
37
|
+
- lib/smart_ass/ycbcr_converter.rb
|
38
|
+
- smart_ass.gemspec
|
39
|
+
- spec/ass_spec.rb
|
40
|
+
- spec/fixtures/info_0
|
41
|
+
- spec/fixtures/info_1_t_3
|
42
|
+
- spec/fixtures/info_3_t_3_5_6
|
43
|
+
- spec/rgba_color_spec.rb
|
44
|
+
- spec/spec_helper.rb
|
45
|
+
- spec/ycbcr_converter_spec.rb
|
46
|
+
homepage: http://github.com/pigoz/smart_ass
|
47
|
+
licenses: []
|
48
|
+
post_install_message:
|
49
|
+
rdoc_options: []
|
50
|
+
require_paths:
|
51
|
+
- lib
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ! '>='
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
|
+
none: false
|
60
|
+
requirements:
|
61
|
+
- - ! '>='
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '0'
|
64
|
+
requirements: []
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 1.8.24
|
67
|
+
signing_key:
|
68
|
+
specification_version: 3
|
69
|
+
summary: Utility for YCbCr ColorMatrix conversion of ASS Subtitles.
|
70
|
+
test_files:
|
71
|
+
- spec/ass_spec.rb
|
72
|
+
- spec/fixtures/info_0
|
73
|
+
- spec/fixtures/info_1_t_3
|
74
|
+
- spec/fixtures/info_3_t_3_5_6
|
75
|
+
- spec/rgba_color_spec.rb
|
76
|
+
- spec/spec_helper.rb
|
77
|
+
- spec/ycbcr_converter_spec.rb
|