vtt2ass 0.2.4 → 0.2.10

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.
@@ -86,7 +86,7 @@
86
86
 
87
87
 
88
88
 
89
- <strong class="classes">Classes:</strong> <span class='object_link'><a href="ASSFile.html" title="ASSFile (class)">ASSFile</a></span>, <span class='object_link'><a href="ASSStyle.html" title="ASSStyle (class)">ASSStyle</a></span>, <span class='object_link'><a href="ASSSubtitle.html" title="ASSSubtitle (class)">ASSSubtitle</a></span>, <span class='object_link'><a href="Application.html" title="Application (class)">Application</a></span>, <span class='object_link'><a href="VTTSubtitle.html" title="VTTSubtitle (class)">VTTSubtitle</a></span>
89
+ <strong class="classes">Classes:</strong> <span class='object_link'><a href="ASSFile.html" title="ASSFile (class)">ASSFile</a></span>, <span class='object_link'><a href="ASSLine.html" title="ASSLine (class)">ASSLine</a></span>, <span class='object_link'><a href="ASSStyle.html" title="ASSStyle (class)">ASSStyle</a></span>, <span class='object_link'><a href="ASSStyleParams.html" title="ASSStyleParams (class)">ASSStyleParams</a></span>, <span class='object_link'><a href="Application.html" title="Application (class)">Application</a></span>, <span class='object_link'><a href="VTTFile.html" title="VTTFile (class)">VTTFile</a></span>, <span class='object_link'><a href="VTTLine.html" title="VTTLine (class)">VTTLine</a></span>
90
90
 
91
91
 
92
92
  </p>
@@ -102,9 +102,9 @@
102
102
  </div>
103
103
 
104
104
  <div id="footer">
105
- Generated on Thu Jan 14 00:31:16 2021 by
105
+ Generated on Tue Mar 23 21:07:02 2021 by
106
106
  <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
107
- 0.9.26 (ruby-2.7.2).
107
+ 0.9.26 (ruby-3.0.0).
108
108
  </div>
109
109
 
110
110
  </div>
data/lib/vtt2ass.rb CHANGED
@@ -17,17 +17,20 @@ module Vtt2ass
17
17
  opts.banner = "Usage: vtt2ass [options]"
18
18
  opts.separator ""
19
19
  opts.separator "Specific options:"
20
- opts.on("-i", "--input PATH", "Specify a custom input file or directory (default: './')") do |dir|
21
- options[:input] = dir
20
+ opts.on("-i", "--input PATH", "Specify a custom input file or directory (default: './')") do |file_path|
21
+ options[:input] = file_path
22
22
  end
23
- opts.on("-o", "--output PATH", "Specify a custom output directory (default: './')") do |dir|
24
- options[:output] = dir
23
+ opts.on("-o", "--output PATH", "Specify a custom output directory (default: './')") do |file_path|
24
+ options[:output] = file_path
25
25
  end
26
- opts.on("-f", "--font-family SIZE", String, "Specify a font family for the subtitles (default: 'Open Sans Semibold')") do |size|
27
- options[:font_family] = size
26
+ opts.on("-f", "--font-family FONT", String, "Specify a font family for the subtitles (default: 'Open Sans Semibold')") do |font_family|
27
+ options[:font_family] = font_family
28
28
  end
29
- opts.on("-s", "--font-size SIZE", Integer, "Specify a font size for the subtitles (default: 52)") do |size|
30
- options[:font_size] = size
29
+ opts.on("-s", "--font-size SIZE", Integer, "Specify a font size for the subtitles (default: 52)") do |font_size|
30
+ options[:font_size] = font_size
31
+ end
32
+ opts.on("-t", "--title TITLE", String, "Specify a title for you file. If the input is a directory, all files will share the same title.") do |title|
33
+ options[:title] = title
31
34
  end
32
35
  opts.on("-v", "--version", "Show version") do
33
36
  puts Vtt2ass::VERSION
@@ -1,15 +1,16 @@
1
+ # Relative imports
2
+ require_relative 'ASSLine'
3
+ require_relative 'ASSStyle'
4
+
1
5
  ##
2
- # This class defines the ASS File that will be created from the conversion.
6
+ # This class defines an ASS subtitle file.
3
7
  class ASSFile
8
+ attr_reader :title, :width, :height
9
+ attr_accessor :ass_styles, :ass_lines
4
10
 
5
11
  ##
6
- # This method creates an instance of the ASSFile.
7
- #
8
- # * Requires +ass_styles+, a list of ASSStyle as input.
9
- # * Requires +ass_subs+, a list of ASSSubtitles as input.
10
- # * Requires a video +width+ as input.
11
- # * Requires a video +height+ as input.
12
- def initialize(title, ass_styles, ass_subs, width, height)
12
+ # Creates a new ASSFile instance and assigns the default values of instance variables.
13
+ def initialize(title, width, height)
13
14
  @width = width
14
15
  @height = height
15
16
  @header = [
@@ -26,18 +27,47 @@ class ASSFile
26
27
  '[V4+ Styles]',
27
28
  'Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding'
28
29
  ]
29
- @ass_styles = ass_styles
30
30
  @events = [
31
31
  '',
32
32
  '[Events]',
33
33
  'Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text'
34
34
  ]
35
- @ass_subs = ass_subs
35
+ @ass_styles = []
36
+ @ass_lines = []
37
+ end
38
+
39
+ ##
40
+ # This method receives a VTTFile object and font arguments creates new ASSLine with the params of
41
+ # each VTTLine. All those ASSLine are stored in an array. It also creates an array of ASSStyle that
42
+ # will be used in the ASS style list.
43
+ def convertVTTtoASS(vtt_file, font_family, font_size)
44
+ vtt_file.lines.each do |line|
45
+ @ass_lines.push(ASSLine.new(line.style, line.time_start, line.time_end, line.text))
46
+ style_exists = false
47
+ @ass_styles.each do |style|
48
+ if (style.style_name == line.style) then
49
+ style_exists = true
50
+ break
51
+ end
52
+ end
53
+ if not style_exists then
54
+ @ass_styles.push(ASSStyle.new(line.style, line.params, font_family, font_size, @width, @height))
55
+ end
56
+ end
57
+ end
58
+
59
+ ##
60
+ # This method writes the content of the ASSFile object into a file path that is provided.
61
+ def writeToFile(file_path)
62
+ File.open(file_path, 'w') do |line|
63
+ line.print "\ufeff"
64
+ line.puts self.to_s
65
+ end
36
66
  end
37
67
 
38
68
  ##
39
69
  # This method concatenates the object data in the right order for a string output.
40
70
  def to_s
41
- return @header + @ass_styles + @events + @ass_subs
71
+ return @header + @ass_styles + @events + @ass_lines
42
72
  end
43
73
  end
@@ -1,20 +1,21 @@
1
+ require 'htmlentities'
2
+
1
3
  ##
2
4
  # This class defines an ASS subtile line.
3
- class ASSSubtitle
4
- attr_reader :style, :time_start, :time_end, :params, :text
5
+ class ASSLine
6
+ attr_reader :style, :time_start, :time_end, :text
5
7
 
6
8
  ##
7
- # This method creates an instance of an ASSSubtitle.
9
+ # This method creates an instance of an ASSLine.
8
10
  #
9
11
  # * Requires a +style+ name as input.
10
12
  # * Requires +time_start+, a VTT formatted timestamp as input.
11
13
  # * Requires +time_start+, a VTT formatted timestamp as input.
12
14
  # * Requires +text+, a VTT formatted string as input.
13
- def initialize(style, time_start, time_end, params, text)
15
+ def initialize(style, time_start, time_end, text)
14
16
  @style = style
15
17
  @time_start = convertTime(time_start)
16
18
  @time_end = convertTime(time_end)
17
- @params = params
18
19
  @text = convertToAssText(text)
19
20
  end
20
21
 
@@ -29,6 +30,7 @@ class ASSSubtitle
29
30
  #
30
31
  # * Requires +text+, a string of VTT formated text as input.
31
32
  def convertToAssText(text)
33
+ decoder = HTMLEntities.new()
32
34
  text = text
33
35
  .gsub(/\r/, '')
34
36
  .gsub(/\n/, '\\N')
@@ -43,7 +45,7 @@ class ASSSubtitle
43
45
  .gsub(/<[^>]>/, '')
44
46
  .gsub(/\\N$/, '')
45
47
  .gsub(/ +$/, '')
46
- return text
48
+ return decoder.decode(text)
47
49
  end
48
50
 
49
51
  ##
@@ -81,7 +83,7 @@ class ASSSubtitle
81
83
  end
82
84
 
83
85
  ##
84
- # The method pads text so that time numbers are a fixed number of digit.
86
+ # This method pads text so that time numbers are a fixed number of digit.
85
87
  #
86
88
  # * Requires +sep+, a string separator.
87
89
  # * Requires +input+, an integer.
@@ -1,3 +1,6 @@
1
+ # Relative imports
2
+ require_relative 'ASSStyleParams'
3
+
1
4
  ##
2
5
  # This class defines an ASS style that can be applied on a subtitle line.
3
6
  class ASSStyle
@@ -16,72 +19,9 @@ class ASSStyle
16
19
  @font_family = font_family
17
20
  @font_size = font_size
18
21
  @style_name = style_name
19
- @s_params = StyleParams.new(params, width, height)
22
+ @s_params = ASSStyleParams.new(params, width, height)
20
23
  end
21
24
 
22
- class StyleParams
23
- attr_reader :horizontal_margin, :vertical_margin, :alignment
24
- def initialize(params, width, height)
25
- (params.split(' ').map { |p| p.split(':') }).each do |p|
26
- case p[0]
27
- when 'position'
28
- @position = p[1].gsub(/%/, '').to_i
29
- when 'line'
30
- @line = p[1].gsub(/%/, '').to_i
31
- @line = @line == -1 ? 100 : @line;
32
- when 'alignment'
33
- @align = p[1]
34
- end
35
- end
36
- createAlignment
37
- createHorizontalMargin(width)
38
- createVerticalMargin(height)
39
- end
40
- def createAlignment
41
- if (defined?(@line) and not defined?(@position)) then
42
- if (defined?(@align)) then
43
- case @align
44
- when 'left'
45
- when 'start'
46
- @alignment = @line >= 50 ? 1 : 7
47
- when 'right'
48
- when 'end'
49
- @alignment = @line >= 50 ? 3 : 9
50
- when 'center'
51
- when 'middle'
52
- @alignment = @line >= 50 ? 2 : 8
53
- end
54
- else
55
- @alignment = @line >= 50 ? 2 : 8 # If position is higher than 50% align to bottom center, else align to top center
56
- end
57
- elsif (defined?(@line) and defined?(@position)) then
58
- @alignment = 1
59
- else
60
- @alignment = 2
61
- end
62
- end
63
- def createHorizontalMargin(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
70
- end
71
- def createVerticalMargin(height)
72
- steps = (height / 100).to_i
73
- if defined?(@line) then
74
- if (@alignment == 1) then
75
- @vertical_margin = (100 - @line) * steps
76
- else
77
- @vertical_margin = @line >= 50 ? (100 - @line) * steps : @line * steps
78
- end
79
- else
80
- @vertical_margin = 50
81
- end
82
- end
83
- end
84
-
85
25
  ##
86
26
  # This method assigns the object values to an ASS style line and outputs it.
87
27
  def to_s
@@ -0,0 +1,87 @@
1
+ ##
2
+ # This class defines the ASS style parameters from VTT cue settings.
3
+ class ASSStyleParams
4
+ attr_reader :horizontal_margin, :vertical_margin, :alignment, :align
5
+
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
+ createAlignment()
23
+ createHorizontalMargin(width)
24
+ createVerticalMargin(height)
25
+ end
26
+
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 createAlignment()
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
58
+ end
59
+
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 createHorizontalMargin(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
70
+ end
71
+
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 createVerticalMargin(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
83
+ else
84
+ @vertical_margin = 50
85
+ end
86
+ end
87
+ end
@@ -2,9 +2,7 @@
2
2
  require 'os'
3
3
 
4
4
  # Relative imports
5
- require_relative 'VTTSubtitle'
6
- require_relative 'ASSSubtitle'
7
- require_relative 'ASSStyle'
5
+ require_relative 'VTTFile'
8
6
  require_relative 'ASSFile'
9
7
 
10
8
  ##
@@ -15,12 +13,15 @@ class Application
15
13
  # Creates a new Application instance.
16
14
  # It receives +options+ that can define the input and output directories.
17
15
  def initialize(options)
18
- @input = options[:input] ? options[:input].gsub('\\', '/') : "./"
19
- @output = options[:output] ? options[:output].gsub('\\', '/') : "./"
16
+ @input = options[:input] ? options[:input].gsub('\\', '/').delete_suffix('/') : "."
17
+ @output = options[:output] ? options[:output].gsub('\\', '/').delete_suffix('/') : "."
20
18
  @width = 1920
21
19
  @height = 1080
22
20
  @font_family = options[:font_family] ? options[:font_family] : 'Open Sans Semibold'
23
21
  @font_size = options[:font_size] ? options[:font_size] : 52
22
+ if options[:title] then
23
+ @title = options[:title]
24
+ end
24
25
  end
25
26
 
26
27
  ##
@@ -30,62 +31,27 @@ class Application
30
31
  def start
31
32
  if File.directory?(@input) then
32
33
  Dir["#{@input}/*.vtt"].each do |file_path|
33
- writeFile(file_path)
34
+ vtt_to_ass(file_path).writeToFile(@output + '/' + File.basename(file_path).gsub('.vtt', '.ass'))
34
35
  end
35
36
  elsif File.file?(@input) then
36
- writeFile(@input)
37
+ vtt_to_ass(@input).writeToFile(@output + '/' + File.basename(@input).gsub('.vtt', '.ass'))
37
38
  else
38
39
  puts 'Error: input file or directory does not exist.'
39
40
  end
40
41
  end
41
42
 
42
- def writeFile(file_path)
43
- file_name = File.basename(file_path).gsub('.vtt', '.ass')
44
- File.open("#{@output}/" + file_name, 'w') do |line|
45
- line.print "\ufeff"
46
- line.puts convertFileToASS(file_path)
47
- end
48
- end
49
-
50
43
  ##
51
- # This method reads the VTT file and sends back a list of paragraphs.
52
- # It requires a +file_path+ as input.
53
- # It outputs a list named list_paragraph.
54
- def readVTTFile(file_path)
55
- list_parapraph = []
56
- separator = OS.posix? ? "\r\n\r\n": "\n\n"
57
- File.foreach(file_path, separator) do |paragraph|
58
- paragraph = paragraph.rstrip.gsub(/\r\n/, "\n")
59
- if not paragraph.eql? "" then
60
- list_parapraph.push(VTTSubtitle.new(paragraph))
61
- end
62
- end
63
- list_parapraph.shift
64
- return list_parapraph
44
+ # This method creates a new VTTFile object from the file path provided and convert its content
45
+ # inside a new ASSFile object.
46
+ def vtt_to_ass(file_path)
47
+ vtt_file = VTTFile.new(file_path)
48
+ ass_file = ASSFile.new(
49
+ (defined?(@title) ? @title : File.basename(file_path).gsub('.vtt', '')),
50
+ @width,
51
+ @height
52
+ )
53
+ ass_file.convertVTTtoASS(vtt_file, @font_family, @font_size)
54
+ return ass_file
65
55
  end
66
56
 
67
- ##
68
- # This method gets the list of paragraphs from the VTT file and creates lists of ASSSubtitle and ASSStyles objects from them.
69
- # Those lists are given a new ASSFile object to generate the file content.
70
- # It requires a +file_path+ as input.
71
- # It outputs an ASSFile object.
72
- def convertFileToASS(file_path)
73
- vtt_subs = readVTTFile(file_path)
74
- ass_subs = []
75
- ass_styles = []
76
- vtt_subs.each do |sub|
77
- ass_subs.push(ASSSubtitle.new(sub.style, sub.time_start, sub.time_end, sub.params, sub.text))
78
- style_exists = false
79
- ass_styles.each do |style|
80
- if (style.style_name == sub.style) then
81
- style_exists = true
82
- break
83
- end
84
- end
85
- if not style_exists then
86
- ass_styles.push(ASSStyle.new(sub.style, sub.params, @font_family, @font_size, @width, @height))
87
- end
88
- end
89
- return ASSFile.new(File.basename(file_path).gsub('.vtt', ''), ass_styles, ass_subs, @width, @height).to_s
90
- end
91
57
  end