vtt2ass 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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