vtt2ass 0.3.4 → 0.3.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/.rubocop.yml +19 -0
- data/Gemfile +2 -0
- data/README.md +5 -1
- data/Rakefile +8 -6
- data/bin/vtt2ass +2 -1
- data/exe/vtt2ass +2 -1
- data/lib/vtt2ass/application.rb +69 -65
- data/lib/vtt2ass/ass_file.rb +91 -94
- data/lib/vtt2ass/ass_line.rb +89 -86
- data/lib/vtt2ass/ass_style.rb +42 -38
- data/lib/vtt2ass/ass_style_params.rb +97 -75
- data/lib/vtt2ass/css_file.rb +28 -26
- data/lib/vtt2ass/css_rule.rb +25 -17
- data/lib/vtt2ass/validator.rb +12 -8
- data/lib/vtt2ass/version.rb +3 -1
- data/lib/vtt2ass/vtt_file.rb +45 -45
- data/lib/vtt2ass/vtt_line.rb +37 -37
- data/lib/vtt2ass.rb +66 -21
- data/vtt2ass.gemspec +20 -16
- metadata +31 -15
data/lib/vtt2ass/ass_style.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require_relative 'ass_style_params'
|
3
4
|
require_relative 'validator'
|
4
5
|
require 'redgreenblue'
|
@@ -6,43 +7,46 @@ require 'redgreenblue'
|
|
6
7
|
##
|
7
8
|
# This class defines an ASS style that can be applied on a subtitle line.
|
8
9
|
class ASSStyle
|
9
|
-
|
10
|
+
attr_reader :style_name
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
@is_italic = is_italic
|
33
|
-
@is_bold = is_bold
|
34
|
-
end
|
12
|
+
##
|
13
|
+
# This method creates and instance of an ASSStyle.
|
14
|
+
#
|
15
|
+
# * Requires +style_name+, a string name for the style as input.
|
16
|
+
# * Requires +params+, a string of VTT styling as input.
|
17
|
+
# * Requires a video +width+ as input.
|
18
|
+
# * Requires a video +height+ as input.
|
19
|
+
def initialize(style_name, params, font_family, font_size, font_color, is_bold, is_italic, offset, width, height)
|
20
|
+
@width = width
|
21
|
+
@height = height
|
22
|
+
@font_family = font_family
|
23
|
+
@font_size = font_size
|
24
|
+
@font_color = font_color
|
25
|
+
@style_name = style_name
|
26
|
+
@s_params = ASSStyleParams.new(params, width, height)
|
27
|
+
@s_params.vertical_margin = 50 if style_name.eql? 'MainTop'
|
28
|
+
@s_params.vertical_margin -= offset[:line] if style_name.include?('Subtitle') || style_name.eql?('Main')
|
29
|
+
@s_params.vertical_margin += offset[:caption] if style_name.include?('Caption') || style_name.eql?('MainTop')
|
30
|
+
@is_italic = is_italic
|
31
|
+
@is_bold = is_bold
|
32
|
+
end
|
35
33
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
34
|
+
##
|
35
|
+
# This method assigns the object values to an ASS style line and outputs it.
|
36
|
+
def to_s
|
37
|
+
# Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour,
|
38
|
+
# Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment,
|
39
|
+
# MarginL, MarginR, MarginV, Encoding
|
40
|
+
"Style: #{@style_name},#{@font_family},#{@font_size},#{@font_color},&H000000FF,&H00020713,&H00000000,"\
|
41
|
+
"#{@is_bold ? '-1' : '0'},#{@is_italic ? '-1' : '0'},0,0,100,100,0,0,1,2.0,2.0,#{@s_params.alignment},"\
|
42
|
+
"#{@s_params.horizontal_margin},0,#{@s_params.vertical_margin},1"
|
43
|
+
end
|
42
44
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
45
|
+
##
|
46
|
+
# This method returns a ASS formated color value based on hex or color name value
|
47
|
+
def self.convert_color(color_value)
|
48
|
+
color_value.gsub!('#', '')
|
49
|
+
color = Validator.hex?(color_value) ? RGB.hex(color_value) : RGB.css(color_value)
|
50
|
+
format('&H00%<blue>02x%<green>02x%<red>02x', blue: color.b, green: color.g, red: color.r).upcase
|
51
|
+
end
|
52
|
+
end
|
@@ -1,87 +1,109 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
##
|
2
4
|
# This class defines the ASS style parameters from VTT cue settings.
|
3
5
|
class ASSStyleParams
|
4
|
-
|
6
|
+
attr_accessor :horizontal_margin, :vertical_margin, :alignment, :align
|
5
7
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
8
|
+
##
|
9
|
+
# Creates an instance of ASSStyleParams
|
10
|
+
# It takes VTT style arguments and assign them to their respectful instance variable.
|
11
|
+
# It calls methods to create ASS values from the VTT cue settings.
|
12
|
+
def initialize(params, width, height)
|
13
|
+
@align = nil
|
14
|
+
split_params(params)
|
15
|
+
create_alignment
|
16
|
+
create_horizontal_margin(width)
|
17
|
+
create_vertical_margin(height)
|
18
|
+
end
|
19
|
+
|
20
|
+
def split_params(params)
|
21
|
+
(params.split.map { |p| p.split(':') }).each do |p|
|
22
|
+
case p[0]
|
23
|
+
when 'position'
|
24
|
+
@position = p[1].gsub(/%/, '').to_i
|
25
|
+
when 'line'
|
26
|
+
@line = p[1].gsub(/%/, '').to_i
|
27
|
+
@line = @line == -1 ? 100 : @line
|
28
|
+
when 'align'
|
29
|
+
@align = p[1].chomp
|
30
|
+
end
|
25
31
|
end
|
32
|
+
end
|
26
33
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
end
|
57
|
-
end
|
34
|
+
##
|
35
|
+
# This method decides the alignement value in a 9 position grid based of the
|
36
|
+
# values in cue settings "align" and "line".
|
37
|
+
def create_alignment
|
38
|
+
@alignment =
|
39
|
+
if defined?(@line) && !defined?(@position)
|
40
|
+
find_alignment(@align)
|
41
|
+
elsif defined?(@line) && defined?(@position)
|
42
|
+
1 # bottom left
|
43
|
+
else
|
44
|
+
find_default_alignment(@align)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
##
|
49
|
+
# This method returns alignment when "line" value is specified but not "position"
|
50
|
+
def find_alignment(align)
|
51
|
+
if align.nil?
|
52
|
+
# If position is higher than 50% align to bottom center, else align to top center
|
53
|
+
@line >= 50 ? 2 : 8
|
54
|
+
else
|
55
|
+
case align
|
56
|
+
when 'left', 'start'
|
57
|
+
@line >= 50 ? 1 : 7
|
58
|
+
when 'right', 'end'
|
59
|
+
@line >= 50 ? 3 : 9
|
60
|
+
when 'center', 'middle'
|
61
|
+
@line >= 50 ? 2 : 8
|
62
|
+
end
|
58
63
|
end
|
64
|
+
end
|
59
65
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
66
|
+
##
|
67
|
+
# This method returns alignment when "line" and "position" values are not specified
|
68
|
+
def find_default_alignment(align)
|
69
|
+
case align
|
70
|
+
when 'left', 'start'
|
71
|
+
1
|
72
|
+
when 'right', 'end'
|
73
|
+
3
|
74
|
+
# when 'center', 'middle'
|
75
|
+
else
|
76
|
+
2
|
70
77
|
end
|
78
|
+
end
|
71
79
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
80
|
+
##
|
81
|
+
# This method calculates the horizontal margin in px between the alignement position and
|
82
|
+
# and the content displayed by using the "position" cue setting.
|
83
|
+
def create_horizontal_margin(width)
|
84
|
+
steps = (width / 100).to_i
|
85
|
+
@horizontal_margin =
|
86
|
+
if defined?(@position)
|
87
|
+
@position * steps
|
88
|
+
else
|
89
|
+
0
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
##
|
94
|
+
# This method calculates the vertical margin in px between the alignement position and
|
95
|
+
# and the content displayed by using the "line" cue setting.
|
96
|
+
def create_vertical_margin(height)
|
97
|
+
steps = (height / 100).to_i
|
98
|
+
@vertical_margin =
|
99
|
+
if defined?(@line)
|
100
|
+
if @alignment == 1
|
101
|
+
(100 - @line) * steps
|
83
102
|
else
|
84
|
-
|
103
|
+
@line >= 50 ? (100 - @line) * steps : @line * steps
|
85
104
|
end
|
86
|
-
|
87
|
-
|
105
|
+
else
|
106
|
+
50
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
data/lib/vtt2ass/css_file.rb
CHANGED
@@ -1,36 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'css_parser'
|
2
4
|
require_relative 'css_rule'
|
3
5
|
|
6
|
+
##
|
7
|
+
# This class defines a CSS file for subtitles.
|
4
8
|
class CSSFile
|
5
|
-
|
6
|
-
include CssParser
|
9
|
+
attr_reader :rules
|
7
10
|
|
8
|
-
|
9
|
-
@file_path = file_path
|
10
|
-
parser = CssParser::Parser.new
|
11
|
-
parser.load_file!(file_path)
|
12
|
-
@rules = []
|
13
|
-
parser.each_selector do |selector, declarations, specificity|
|
14
|
-
css_obj = CSSRule.new(selector, declarations)
|
15
|
-
if not css_obj.name.empty? then
|
16
|
-
@rules.push(css_obj)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
11
|
+
include CssParser
|
20
12
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
return return_rule
|
13
|
+
def initialize(file_path)
|
14
|
+
@file_path = file_path
|
15
|
+
parser = CssParser::Parser.new
|
16
|
+
parser.load_file!(file_path)
|
17
|
+
@rules = []
|
18
|
+
parser.each_selector do |selector, declarations, _specificity|
|
19
|
+
css_obj = CSSRule.new(selector, declarations)
|
20
|
+
@rules.push(css_obj) unless css_obj.name.empty?
|
30
21
|
end
|
22
|
+
end
|
31
23
|
|
32
|
-
|
33
|
-
|
24
|
+
def find_rule(value)
|
25
|
+
return_rule = nil
|
26
|
+
@rules.each do |rule|
|
27
|
+
if rule.name == value
|
28
|
+
return_rule = rule
|
29
|
+
break
|
30
|
+
end
|
34
31
|
end
|
32
|
+
return_rule
|
33
|
+
end
|
35
34
|
|
36
|
-
|
35
|
+
def to_s
|
36
|
+
@file_path
|
37
|
+
end
|
38
|
+
end
|
data/lib/vtt2ass/css_rule.rb
CHANGED
@@ -1,22 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
##
|
4
|
+
# This class defines a CSS rule that is included in the CSS file.
|
1
5
|
class CSSRule
|
2
|
-
|
6
|
+
attr_reader :name, :properties
|
3
7
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
end
|
8
|
+
def initialize(selector, declarations)
|
9
|
+
@name = reduce_selector(selector)
|
10
|
+
@properties = []
|
11
|
+
declarations.split(/;\s?/).each do |dec|
|
12
|
+
temp = dec.split(/:\s?/)
|
13
|
+
@properties.push(
|
14
|
+
{ key: temp.first, value: temp.last }
|
15
|
+
)
|
13
16
|
end
|
17
|
+
end
|
14
18
|
|
15
|
-
|
16
|
-
|
17
|
-
|
19
|
+
def to_s
|
20
|
+
"#{@name} #{@properties}"
|
21
|
+
end
|
18
22
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
+
##
|
24
|
+
# This method removes the generic selector from a block.
|
25
|
+
def reduce_selector(selector)
|
26
|
+
selector.to_s.gsub(
|
27
|
+
/\.rmp-container>\.rmp-content>\.rmp-cc-area>\.rmp-cc-container>\.rmp-cc-display>\.rmp-cc-cue\s?\.?/, ''
|
28
|
+
)
|
29
|
+
end
|
30
|
+
end
|
data/lib/vtt2ass/validator.rb
CHANGED
@@ -1,10 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
##
|
4
|
+
# This class defines validation tools for data.
|
1
5
|
class Validator
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
end
|
8
|
-
return hex
|
6
|
+
def self.hex?(value)
|
7
|
+
hex = true
|
8
|
+
value.gsub!('#', '')
|
9
|
+
value.chars.each do |digit|
|
10
|
+
hex = false unless digit.match(/\h/)
|
9
11
|
end
|
10
|
-
|
12
|
+
hex
|
13
|
+
end
|
14
|
+
end
|
data/lib/vtt2ass/version.rb
CHANGED
data/lib/vtt2ass/vtt_file.rb
CHANGED
@@ -1,58 +1,58 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require_relative 'vtt_line'
|
3
4
|
|
4
5
|
##
|
5
6
|
# This class defines a VTT subtile file.
|
6
7
|
class VTTFile
|
7
|
-
|
8
|
+
attr_accessor :lines
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
end
|
28
|
-
@lines.push(vtt_line)
|
29
|
-
count += 1
|
30
|
-
end
|
10
|
+
##
|
11
|
+
# Creates a new VTTFile instance and assigns the default values of instance variables.
|
12
|
+
def initialize(file_path, width, height)
|
13
|
+
@title = File.basename(file_path).gsub('.vtt', '')
|
14
|
+
@lines = []
|
15
|
+
separator = determine_line_ending(file_path) ? "\n\n" : "\r\n\r\n"
|
16
|
+
count = 0
|
17
|
+
style_count = 1
|
18
|
+
File.foreach(file_path, separator) do |paragraph|
|
19
|
+
paragraph = paragraph.rstrip.gsub(/[\r\n]/, "\n")
|
20
|
+
unless paragraph.eql? ''
|
21
|
+
vtt_line = VTTLine.new(paragraph, width, height)
|
22
|
+
if vtt_line.style.eql?('Main') &&
|
23
|
+
!vtt_line.params.to_s.empty? &&
|
24
|
+
(!vtt_line.params.to_s.eql?('align:middle') &&
|
25
|
+
!vtt_line.params.to_s.eql?('align:center'))
|
26
|
+
vtt_line.style = "Style#{style_count}"
|
27
|
+
style_count += 1
|
31
28
|
end
|
32
|
-
@lines.
|
29
|
+
@lines.push(vtt_line)
|
30
|
+
count += 1
|
31
|
+
end
|
33
32
|
end
|
33
|
+
@lines.shift
|
34
|
+
end
|
34
35
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
##
|
44
|
-
# This method writes the content of the VTTFile object into a file path that is provided.
|
45
|
-
def write_to_file(file_path)
|
46
|
-
File.open(file_path, 'w') do |line|
|
47
|
-
line.print "\ufeff"
|
48
|
-
line.puts self.to_s
|
49
|
-
end
|
36
|
+
##
|
37
|
+
# This method determines the line ending character to use as a separator.
|
38
|
+
def determine_line_ending(file_path)
|
39
|
+
File.open(file_path, 'r') do |file|
|
40
|
+
return file.readline[/\r?\n$/] == "\n"
|
50
41
|
end
|
42
|
+
end
|
51
43
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
44
|
+
##
|
45
|
+
# This method writes the content of the VTTFile object into a file path that is provided.
|
46
|
+
def write_to_file(file_path)
|
47
|
+
File.open(file_path, 'w') do |line|
|
48
|
+
line.print "\ufeff"
|
49
|
+
line.puts to_s
|
56
50
|
end
|
51
|
+
end
|
57
52
|
|
58
|
-
|
53
|
+
##
|
54
|
+
# This method concatenates the object data in the right order for a string output.
|
55
|
+
def to_s
|
56
|
+
"WEBVTT\n\n\n#{@lines}"
|
57
|
+
end
|
58
|
+
end
|
data/lib/vtt2ass/vtt_line.rb
CHANGED
@@ -1,44 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
##
|
2
4
|
# This class defines a VTT subtile line.
|
3
5
|
class VTTLine
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
##
|
8
|
-
# This method creates an instance of an VTTLine.
|
9
|
-
#
|
10
|
-
# * Requires +paragraph+, a VTT formatted string as input.
|
11
|
-
def initialize(paragraph, width, height)
|
12
|
-
lines = paragraph.split("\n")
|
13
|
-
rx = /^([\d:.]*) --> ([\d:.]*)\s?(.*?)\s*$/
|
14
|
-
@style = "Main"
|
15
|
-
@text, @time_start, @time_end, @params = ""
|
16
|
-
count = 0
|
6
|
+
attr_accessor :style
|
7
|
+
attr_reader :time_start, :time_end, :params, :text
|
17
8
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
@style = 'MainTop'
|
29
|
-
end
|
30
|
-
else
|
31
|
-
@text += line + "\n"
|
32
|
-
end
|
33
|
-
count += 1;
|
34
|
-
end
|
9
|
+
##
|
10
|
+
# This method creates an instance of an VTTLine.
|
11
|
+
#
|
12
|
+
# * Requires +paragraph+, a VTT formatted string as input.
|
13
|
+
def initialize(paragraph, width, height)
|
14
|
+
lines = paragraph.split("\n")
|
15
|
+
rx = /^([\d:.]*) --> ([\d:.]*)\s?(.*?)\s*$/
|
16
|
+
@style = 'Main'
|
17
|
+
@text, @time_start, @time_end, @params = ''
|
18
|
+
count = 0
|
35
19
|
|
36
|
-
|
20
|
+
lines.each do |line|
|
21
|
+
m = line.match(rx)
|
22
|
+
if !m && count.zero?
|
23
|
+
@style = line
|
24
|
+
elsif m
|
25
|
+
@time_start = m[1]
|
26
|
+
@time_end = m[2]
|
27
|
+
@params = m[3]
|
28
|
+
ass_style = ASSStyleParams.new(@params, width, height)
|
29
|
+
@style = 'MainTop' if @style.eql?('Main') && ass_style.alignment == 8
|
30
|
+
else
|
31
|
+
@text += "#{line}\n"
|
32
|
+
end
|
33
|
+
count += 1
|
37
34
|
end
|
38
35
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
36
|
+
@text = @text.lstrip
|
37
|
+
end
|
38
|
+
|
39
|
+
##
|
40
|
+
# This method assigns the object values and outputs a VTT dialogue line.
|
41
|
+
def to_s
|
42
|
+
"#{@style} \n#{@time_start} --> #{@time_end} #{@params}\n#{@text}"
|
43
|
+
end
|
44
|
+
end
|