vtt2ass 0.3.4 → 0.3.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,5 @@
1
- # Relative imports
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
- attr_reader :style_name
10
+ attr_reader :style_name
10
11
 
11
- ##
12
- # This method creates and instance of an ASSStyle.
13
- #
14
- # * Requires +style_name+, a string name for the style as input.
15
- # * Requires +params+, a string of VTT styling as input.
16
- # * Requires a video +width+ as input.
17
- # * Requires a video +height+ as input.
18
- def initialize(style_name, params, font_family, font_size, font_color, is_bold, is_italic, line_offset, width, height)
19
- @width = width
20
- @height = height
21
- @font_family = font_family
22
- @font_size = font_size
23
- @font_color = font_color
24
- @style_name = style_name
25
- @s_params = ASSStyleParams.new(params, width, height)
26
- if style_name.eql? 'MainTop' then
27
- @s_params.vertical_margin = 50
28
- end
29
- if style_name.include? 'Subtitle' then
30
- @s_params.vertical_margin -= line_offset
31
- end
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
- # This method assigns the object values to an ASS style line and outputs it.
38
- def to_s
39
- # Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
40
- return "Style: #{@style_name},#{@font_family},#{@font_size},#{@font_color},&H000000FF,&H00020713,&H00000000,#{@is_bold ? '-1' : '0'},#{@is_italic ? '-1' : '0'},0,0,100,100,0,0,1,2.0,2.0,#{@s_params.alignment},#{@s_params.horizontal_margin},0,#{@s_params.vertical_margin},1"
41
- end
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
- def self.convert_color(color_value)
44
- color_value.gsub!('#', '')
45
- color = Validator.hex?(color_value) ? RGB.hex(color_value) : RGB.css(color_value)
46
- return ("&H00%02x%02x%02x" % [color.b, color.g, color.r]).upcase
47
- end
48
- end
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
- attr_accessor :horizontal_margin, :vertical_margin, :alignment, :align
6
+ attr_accessor :horizontal_margin, :vertical_margin, :alignment, :align
5
7
 
6
- ##
7
- # Creates an instance of ASSStyleParams
8
- # It takes VTT style arguments and assign them to their respectful instance variable.
9
- # It calls methods to create ASS values from the VTT cue settings.
10
- def initialize(params, width, height)
11
- (params.split(' ').map { |p| p.split(':') }).each do |p|
12
- case p[0]
13
- when 'position'
14
- @position = p[1].gsub(/%/, '').to_i
15
- when 'line'
16
- @line = p[1].gsub(/%/, '').to_i
17
- @line = @line == -1 ? 100 : @line;
18
- when 'align'
19
- @align = p[1].chomp
20
- end
21
- end
22
- create_alignment()
23
- create_horizontal_margin(width)
24
- create_vertical_margin(height)
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
- # This method decides the alignement value in a 9 position grid based of the
29
- # values in cue settings "align" and "line".
30
- def create_alignment()
31
- if (defined?(@line) and not defined?(@position)) then
32
- if (defined?(@align)) then
33
- case @align
34
- when 'left', 'start'
35
- @alignment = @line >= 50 ? 1 : 7
36
- when 'right', 'end'
37
- @alignment = @line >= 50 ? 3 : 9
38
- when 'center', 'middle'
39
- @alignment = @line >= 50 ? 2 : 8
40
- end
41
- else
42
- @alignment = @line >= 50 ? 2 : 8 # If position is higher than 50% align to bottom center, else align to top center
43
- end
44
- elsif (defined?(@line) and defined?(@position)) then
45
- @alignment = 1
46
- else
47
- case @align
48
- when 'left', 'start'
49
- @alignment = 1
50
- when 'right', 'end'
51
- @alignment = 3
52
- when 'center', 'middle'
53
- @alignment = 2
54
- else
55
- @alignment = 2
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
- # This method calculates the horizontal margin in px between the alignement position and
62
- # and the content displayed by using the "position" cue setting.
63
- def create_horizontal_margin(width)
64
- steps = (width / 100).to_i
65
- if defined?(@position) then
66
- @horizontal_margin = @position * steps
67
- else
68
- @horizontal_margin = 0
69
- end
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
- # This method calculates the vertical margin in px between the alignement position and
74
- # and the content displayed by using the "line" cue setting.
75
- def create_vertical_margin(height)
76
- steps = (height / 100).to_i
77
- if defined?(@line) then
78
- if (@alignment == 1) then
79
- @vertical_margin = (100 - @line) * steps
80
- else
81
- @vertical_margin = @line >= 50 ? (100 - @line) * steps : @line * steps
82
- end
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
- @vertical_margin = 50
103
+ @line >= 50 ? (100 - @line) * steps : @line * steps
85
104
  end
86
- end
87
- end
105
+ else
106
+ 50
107
+ end
108
+ end
109
+ end
@@ -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
- attr_reader :rules
6
- include CssParser
9
+ attr_reader :rules
7
10
 
8
- def initialize(file_path)
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
- def find_rule(value)
22
- return_rule = nil
23
- @rules.each do |rule|
24
- if rule.name == value then
25
- return_rule = rule
26
- break
27
- end
28
- end
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
- def to_s
33
- return @file_path
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
- end
35
+ def to_s
36
+ @file_path
37
+ end
38
+ end
@@ -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
- attr_reader :name, :properties
6
+ attr_reader :name, :properties
3
7
 
4
- def initialize(selector, declarations)
5
- @name = reduce_selector(selector)
6
- @properties = []
7
- declarations.split(/;\s?/).each do |dec|
8
- temp = dec.split(/:\s?/)
9
- @properties.push(
10
- { key: temp.first, value: temp.last}
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
- def to_s
16
- return "#{@name} #{@properties}"
17
- end
19
+ def to_s
20
+ "#{@name} #{@properties}"
21
+ end
18
22
 
19
- def reduce_selector(selector)
20
- return selector.to_s.gsub(/\.rmp-container>\.rmp-content>\.rmp-cc-area>\.rmp-cc-container>\.rmp-cc-display>\.rmp-cc-cue\s?\.?/, '')
21
- end
22
- end
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
@@ -1,10 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ ##
4
+ # This class defines validation tools for data.
1
5
  class Validator
2
- def self.hex?(value)
3
- hex = true
4
- value.gsub!('#', '')
5
- value.chars.each do |digit|
6
- hex = false unless digit.match(/\h/)
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
- end
12
+ hex
13
+ end
14
+ end
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  ##
2
4
  # Vtt2ass module to provide the version number
3
5
  module Vtt2ass
4
6
  ##
5
7
  # This is the version of the application.
6
8
  # This needs to be changed for each gem release.
7
- VERSION = "0.3.4"
9
+ VERSION = '0.3.6'
8
10
  end
@@ -1,58 +1,58 @@
1
- # Relative imports
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
- attr_accessor :lines
8
+ attr_accessor :lines
8
9
 
9
- ##
10
- # Creates a new VTTFile instance and assigns the default values of instance variables.
11
- def initialize(file_path, width, height)
12
- @title = File.basename(file_path).gsub('.vtt', '')
13
- @lines = []
14
- separator = determine_line_ending(file_path) ? "\n\n" : "\r\n\r\n"
15
- count = 0
16
- style_count = 1
17
- File.foreach(file_path, separator) do |paragraph|
18
- paragraph = paragraph.rstrip.gsub(/[\r\n]/, "\n")
19
- if not paragraph.eql? "" then
20
- vtt_line = VTTLine.new(paragraph, width, height)
21
- if vtt_line.style.eql? 'Main' and
22
- not vtt_line.params.to_s.empty? and
23
- (not vtt_line.params.to_s.eql? 'align:middle' and
24
- not vtt_line.params.to_s.eql? 'align:center') then
25
- vtt_line.style = "Style#{style_count}"
26
- style_count += 1
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.shift
29
+ @lines.push(vtt_line)
30
+ count += 1
31
+ end
33
32
  end
33
+ @lines.shift
34
+ end
34
35
 
35
- ##
36
- # This method determines the line ending character to use as a separator.
37
- def determine_line_ending(file_path)
38
- File.open(file_path, 'r') do |file|
39
- return file.readline[/\r?\n$/] == "\n"
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
- # This method concatenates the object data in the right order for a string output.
54
- def to_s
55
- return "WEBVTT\n\n\n" + @lines
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
- end
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
@@ -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
- attr_accessor :style
5
- attr_reader :time_start, :time_end, :params, :text
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
- lines.each do |line|
19
- m = line.match(rx)
20
- if not m and count == 0 then
21
- @style = line
22
- elsif m then
23
- @time_start = m[1]
24
- @time_end = m[2]
25
- @params = m[3]
26
- ass_style = ASSStyleParams.new(@params, width, height)
27
- if @style.eql? 'Main' and ass_style.alignment == 8 then
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
- @text = @text.lstrip
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
- # This method assigns the object values and outputs a VTT dialogue line.
41
- def to_s
42
- return "#{@style} \n#{@time_start} --> #{@time_end} #{@params}\n#{@text}"
43
- end
44
- end
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