vtt2ass 0.2.1

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.
@@ -0,0 +1,112 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>
7
+ Top Level Namespace
8
+
9
+ &mdash; Documentation by YARD 0.9.26
10
+
11
+ </title>
12
+
13
+ <link rel="stylesheet" href="css/style.css" type="text/css" />
14
+
15
+ <link rel="stylesheet" href="css/common.css" type="text/css" />
16
+
17
+ <script type="text/javascript">
18
+ pathId = "";
19
+ relpath = '';
20
+ </script>
21
+
22
+
23
+ <script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
24
+
25
+ <script type="text/javascript" charset="utf-8" src="js/app.js"></script>
26
+
27
+
28
+ </head>
29
+ <body>
30
+ <div class="nav_wrap">
31
+ <iframe id="nav" src="class_list.html?1"></iframe>
32
+ <div id="resizer"></div>
33
+ </div>
34
+
35
+ <div id="main" tabindex="-1">
36
+ <div id="header">
37
+ <div id="menu">
38
+
39
+ <a href="_index.html">Index</a> &raquo;
40
+
41
+
42
+ <span class="title">Top Level Namespace</span>
43
+
44
+ </div>
45
+
46
+ <div id="search">
47
+
48
+ <a class="full_list_link" id="class_list_link"
49
+ href="class_list.html">
50
+
51
+ <svg width="24" height="24">
52
+ <rect x="0" y="4" width="24" height="4" rx="1" ry="1"></rect>
53
+ <rect x="0" y="12" width="24" height="4" rx="1" ry="1"></rect>
54
+ <rect x="0" y="20" width="24" height="4" rx="1" ry="1"></rect>
55
+ </svg>
56
+ </a>
57
+
58
+ </div>
59
+ <div class="clear"></div>
60
+ </div>
61
+
62
+ <div id="content"><h1>Top Level Namespace
63
+
64
+
65
+
66
+ </h1>
67
+ <div class="box_info">
68
+
69
+
70
+
71
+
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+ </div>
80
+
81
+ <h2>Defined Under Namespace</h2>
82
+ <p class="children">
83
+
84
+
85
+ <strong class="modules">Modules:</strong> <span class='object_link'><a href="Vtt2ass.html" title="Vtt2ass (module)">Vtt2ass</a></span>
86
+
87
+
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>
90
+
91
+
92
+ </p>
93
+
94
+
95
+
96
+
97
+
98
+
99
+
100
+
101
+
102
+ </div>
103
+
104
+ <div id="footer">
105
+ Generated on Thu Jan 14 00:31:16 2021 by
106
+ <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
107
+ 0.9.26 (ruby-2.7.2).
108
+ </div>
109
+
110
+ </div>
111
+ </body>
112
+ </html>
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'vtt2ass'
4
+ Vtt2ass::main
@@ -0,0 +1,40 @@
1
+ # Imports
2
+ require 'optparse'
3
+
4
+ # Relative imports
5
+ require_relative 'vtt2ass/version'
6
+ require_relative 'vtt2ass/Application'
7
+
8
+ module Vtt2ass
9
+ ##
10
+ # This function creates a new application instance and starts the process.
11
+ #
12
+ # It also defines the arguments that can be provided from the CLI.
13
+ def main
14
+ options = {}
15
+
16
+ OptionParser.new do |opts|
17
+ opts.banner = "Usage: vtt2ass [options]"
18
+ opts.separator ""
19
+ opts.separator "Specific options:"
20
+ opts.on("-i", "--input DIRECTORY", "Specify a custom input directory (default: './input')") do |dir|
21
+ options[:input] = dir
22
+ end
23
+ opts.on("-o", "--output DIRECTORY", "Specify a custom output directory (default: './output')") do |dir|
24
+ options[:output] = dir
25
+ end
26
+ opts.on("-s", "--font-size SIZE", Integer, "Specify a font size for the subtitles (default: 72)") do |size|
27
+ options[:font_size] = size
28
+ end
29
+ opts.on("-v", "--version", "Show version") do
30
+ puts Vtt2ass::VERSION
31
+ exit
32
+ end
33
+ end.parse!
34
+
35
+ app = Application.new(options)
36
+ app.start
37
+ end
38
+
39
+ module_function :main
40
+ end
@@ -0,0 +1,43 @@
1
+ ##
2
+ # This class defines the ASS File that will be created from the conversion.
3
+ class ASSFile
4
+
5
+ ##
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(ass_styles, ass_subs, width, height)
13
+ @width = width
14
+ @height = height
15
+ @header = [
16
+ '[Script Info]',
17
+ 'Title: DKB Team',
18
+ 'ScriptType: v4.00+',
19
+ 'Collisions: Normal',
20
+ 'PlayDepth: 0',
21
+ "PlayResX: #{@width}",
22
+ "PlayResY: #{@height}",
23
+ 'WrapStyle: 0',
24
+ 'ScaledBorderAndShadow: yes',
25
+ '',
26
+ '[V4+ Styles]',
27
+ '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
+ @ass_styles = ass_styles
30
+ @events = [
31
+ '',
32
+ '[Events]',
33
+ 'Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text'
34
+ ]
35
+ @ass_subs = ass_subs
36
+ end
37
+
38
+ ##
39
+ # This method concatenates the object data in the right order for a string output.
40
+ def to_s
41
+ return @header + @ass_styles + @events + @ass_subs
42
+ end
43
+ end
@@ -0,0 +1,63 @@
1
+ ##
2
+ # This class defines an ASS style that can be applied on a subtitle line.
3
+ class ASSStyle
4
+ attr_reader :style_name
5
+
6
+ ##
7
+ # This method creates and instance of an ASSStyle.
8
+ #
9
+ # * Requires +style_name+, a string name for the style as input.
10
+ # * Requires +params+, a string of VTT styling as input.
11
+ # * Requires a video +width+ as input.
12
+ # * Requires a video +height+ as input.
13
+ def initialize(style_name, params, font_size, width, height)
14
+ @width = width
15
+ @height = height
16
+ @font_size = font_size
17
+ @style_name = style_name
18
+ assignParams(params)
19
+ end
20
+
21
+ ##
22
+ # This method converts the string of VTT styling in values used for ASS styling.
23
+ #
24
+ # * Requires +params+, a string of VTT styling as input.
25
+ def assignParams(params)
26
+ @alignment = "2"
27
+ @left_margin = "10"
28
+ @right_margin = "10"
29
+ @vertical_margin = "50"
30
+
31
+ if params.include? "align:middle line:7%" then
32
+ @style_name = "MainTop"
33
+ @vertical_margin = "30"
34
+ @alignment = "8"
35
+ else
36
+ param_count = 0
37
+ (params.split(' ').map { |p| p.split(':') }).each do |p|
38
+ case p[0]
39
+ when "position"
40
+ @left_margin = (@width * ((p[1].gsub(/%/, '').to_f - 7) / 100)).to_i.to_s
41
+ when "align"
42
+ case p[1]
43
+ when "left"
44
+ @alignment = 1
45
+ when "middle"
46
+ @alignment = 2
47
+ when "right"
48
+ @alignment = 3
49
+ end
50
+ when "line"
51
+ @vertical_margin = (@height - (@height * ((p[1].gsub(/%/, '').to_f + 7) / 100))).to_i.to_s
52
+ end
53
+ param_count += 1
54
+ end
55
+ end
56
+ end
57
+
58
+ ##
59
+ # This method assigns the object values to an ASS style line and outputs it.
60
+ def to_s
61
+ return "Style: #{@style_name},Open Sans Semibold,#{@font_size},&H00FFFFFF,&H000000FF,&H00020713,&H00000000,-1,0,0,0,100,100,0,0,1,2.0,2.0,#{@alignment},#{@left_margin},#{@right_margin},#{@vertical_margin},1"
62
+ end
63
+ end
@@ -0,0 +1,92 @@
1
+ ##
2
+ # This class defines an ASS subtile line.
3
+ class ASSSubtitle
4
+ attr_reader :style, :time_start, :time_end, :params, :text
5
+
6
+ ##
7
+ # This method creates an instance of an ASSSubtitle.
8
+ #
9
+ # * Requires a +style+ name as input.
10
+ # * Requires +time_start+, a VTT formatted timestamp as input.
11
+ # * Requires +time_start+, a VTT formatted timestamp as input.
12
+ # * Requires +text+, a VTT formatted string as input.
13
+ def initialize(style, time_start, time_end, params, text)
14
+ @style = style
15
+ @time_start = convertTime(time_start)
16
+ @time_end = convertTime(time_end)
17
+ @params = params
18
+ @text = convertToAssText(text)
19
+ end
20
+
21
+ ##
22
+ # This method assigns the object values and outputs an ASS dialogue line.
23
+ def to_s
24
+ return "Dialogue: 0,#{@time_start},#{@time_end},#{@style},,0,0,0,,#{@text}"
25
+ end
26
+
27
+ ##
28
+ # This method replaces characters and tags to ASS compatible characters and tags.
29
+ #
30
+ # * Requires +text+, a string of VTT formated text as input.
31
+ def convertToAssText(text)
32
+ text = text
33
+ .gsub(/\r/, '')
34
+ .gsub(/\n/, '\\N')
35
+ .gsub(/\\n/, '\\N')
36
+ .gsub(/\\N +/, '\\N')
37
+ .gsub(/ +\\N/, '\\N')
38
+ .gsub(/(\\N)+/, '\\N')
39
+ .gsub(/<b[^>]*>([^<]*)<\/b>/) { |s| "{\\b1}#{$1}{\\b0}" }
40
+ .gsub(/<i[^>]*>([^<]*)<\/i>/) { |s| "{\\i1}#{$1}{\\i0}" }
41
+ .gsub(/<u[^>]*>([^<]*)<\/u>/) { |s| "{\\u1}#{$1}{\\u0}" }
42
+ .gsub(/<c[^>]*>([^<]*)<\/c>/) { |s| $1 }
43
+ .gsub(/<[^>]>/, '')
44
+ .gsub(/\\N$/, '')
45
+ .gsub(/ +$/, '')
46
+ return text
47
+ end
48
+
49
+ ##
50
+ # This method validates the time format and sends the matching time to be converted
51
+ #
52
+ # * Requires +str+, a VTT formatted time string.
53
+ def convertTime(time)
54
+ mTime = time.match(/([\d:]*)\.?(\d*)/)
55
+ return toSubsTime(mTime[0])
56
+ end
57
+
58
+ ##
59
+ # This method converts time from VTT format to the ASS format.
60
+ #
61
+ # * Requires +str+, a VTT formatted time string.
62
+ def toSubsTime(str)
63
+ n = []
64
+ x = str.split(/[:.]/).map { |x| x.to_i }
65
+
66
+ msLen = 2
67
+ hLen = 1
68
+
69
+ x[3] = '0.' + (x[3].to_s).rjust(3, '0')
70
+ sx = x[0]*60*60 + x[1]*60 + x[2] + x[3].to_f
71
+ sx = ("%.2f" % sx).split('.')
72
+
73
+ n.unshift(padTimeNum('.', sx[1], msLen))
74
+ sx = sx[0].to_f
75
+
76
+ n.unshift(padTimeNum(':', (sx % 60).to_i, 2))
77
+ n.unshift(padTimeNum(':', (sx / 60).floor % 60, 2))
78
+ n.unshift(padTimeNum('', (sx / 3600).floor % 60, hLen))
79
+
80
+ return n.join('')
81
+ end
82
+
83
+ ##
84
+ # The method pads text so that time numbers are a fixed number of digit.
85
+ #
86
+ # * Requires +sep+, a string separator.
87
+ # * Requires +input+, an integer.
88
+ # * Requires +pad+, an integer for the number of digits to be padded.
89
+ def padTimeNum(sep, input, pad)
90
+ return sep + (input.to_s).rjust(pad, '0')
91
+ end
92
+ end
@@ -0,0 +1,82 @@
1
+ # Imports
2
+ require 'os'
3
+ require 'fileutils'
4
+
5
+ # Relative imports
6
+ require_relative 'VTTSubtitle'
7
+ require_relative 'ASSSubtitle'
8
+ require_relative 'ASSStyle'
9
+ require_relative 'ASSFile'
10
+
11
+ ##
12
+ # Main application class that manages all the operations.
13
+ class Application
14
+
15
+ ##
16
+ # Creates a new Application instance.
17
+ # It receives +options+ that can define the input and output directories.
18
+ def initialize(options)
19
+ @input_dir = options[:input] ? options[:input]: "./input"
20
+ @output_dir = options[:output] ? options[:output]: "./output"
21
+ @width = 1920
22
+ @height = 1080
23
+ @font_size = options[:font_size] ? options[:font_size] : 72
24
+ end
25
+
26
+ ##
27
+ # This method starts the application process.
28
+ # It sends the file_paths of VTT files in the input directory to convertFileToASS method
29
+ # and outputs the resulting ASS format to a new file.
30
+ def start
31
+ Dir["#{@input_dir}/*.vtt"].each do |file_path|
32
+ file_name = File.basename(file_path).gsub('.vtt', '.ass')
33
+ FileUtils.mkdir_p @output_dir
34
+ File.open("#{@output_dir}/" + file_name, 'w') do |line|
35
+ line.print "\ufeff"
36
+ line.puts convertFileToASS(file_path)
37
+ end
38
+ end
39
+ end
40
+
41
+ ##
42
+ # This method reads the VTT file and sends back a list of paragraphs.
43
+ # It requires a +file_path+ as input.
44
+ # It outputs a list named list_paragraph.
45
+ def readVTTFile(file_path)
46
+ list_parapraph = []
47
+ separator = OS.linux? ? "\r\n\r\n": "\n\n"
48
+ File.foreach(file_path, separator) do |paragraph|
49
+ paragraph = paragraph.rstrip.gsub(/\r\n/, "\n")
50
+ if not paragraph.eql? "" then
51
+ list_parapraph.push(VTTSubtitle.new(paragraph))
52
+ end
53
+ end
54
+ list_parapraph.shift
55
+ return list_parapraph
56
+ end
57
+
58
+ ##
59
+ # This method gets the list of paragraphs from the VTT file and creates lists of ASSSubtitle and ASSStyles objects from them.
60
+ # Those lists are given a new ASSFile object to generate the file content.
61
+ # It requires a +file_path+ as input.
62
+ # It outputs an ASSFile object.
63
+ def convertFileToASS(file_path)
64
+ vtt_subs = readVTTFile(file_path)
65
+ ass_subs = []
66
+ ass_styles = []
67
+ vtt_subs.each do |sub|
68
+ ass_subs.push(ASSSubtitle.new(sub.style, sub.time_start, sub.time_end, sub.params, sub.text))
69
+ style_exists = false
70
+ ass_styles.each do |style|
71
+ if (style.style_name == sub.style) then
72
+ style_exists = true
73
+ break
74
+ end
75
+ end
76
+ if not style_exists then
77
+ ass_styles.push(ASSStyle.new(sub.style, sub.params, @font_size, @width, @height))
78
+ end
79
+ end
80
+ return ASSFile.new(ass_styles, ass_subs, @width, @height).to_s
81
+ end
82
+ end
@@ -0,0 +1,40 @@
1
+ ##
2
+ # This class defines a VTT subtile line.
3
+ class VTTSubtitle
4
+ attr_reader :style, :time_start, :time_end, :params, :text
5
+
6
+ ##
7
+ # This method creates an instance of an VTTSubtitle.
8
+ #
9
+ # * Requires +paragraph+, a VTT formatted string as input.
10
+ def initialize(paragraph)
11
+ lines = paragraph.split("\n")
12
+ rx = /^([\d:.]*) --> ([\d:.]*)\s?(.*?)\s*$/
13
+ @style = "Main"
14
+ @text, @time_start, @time_end, @params = ""
15
+ count = 0
16
+
17
+ lines.each do |line|
18
+ m = line.match(rx)
19
+ if not m and count == 0 then
20
+ @style = line
21
+ elsif m then
22
+ @time_start = m[1]
23
+ @time_end = m[2]
24
+ @params = m[3]
25
+ if @params.include? "align:middle line:7%" then
26
+ @style = "MainTop"
27
+ end
28
+ else
29
+ @text += line + "\n"
30
+ end
31
+ count += 1;
32
+ end
33
+ end
34
+
35
+ ##
36
+ # This method assigns the object values and outputs a VTT dialogue line.
37
+ def to_s
38
+ return "#{@style} \n#{@time_start} --> #{@time_end} #{@params}\n#{@text}"
39
+ end
40
+ end