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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bacdcc421becff18a39326c953658fb28c07eb06bd5a4dacc951098e661a9601
|
4
|
+
data.tar.gz: ef37756d00cdfa88d23a778fd2ad0718e1129e74e412aa77a5ceebfd0daeb5f7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 74ffa9400ecd804d00fe2a5649cb9a7e5b0ebb14b2cb7fd1fe4744517b047c4af4e05edf5e0f9ad1b8d34e47ece1d4c8f8e74c749b78f88a193258853fffceb9
|
7
|
+
data.tar.gz: e861fe563ab6ef59085062bd1e2269ca56f20eb939df05eaead50ff78fb754c8339fdb4291c3cf9f357fdd5712df7a2fcbacdf8d4bce2a2773e11c51694d37ce
|
data/.gitignore
CHANGED
data/.rubocop.yml
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
---
|
2
|
+
AllCops:
|
3
|
+
TargetRubyVersion: 2.7.2
|
4
|
+
NewCops: enable
|
5
|
+
# Set method line number to 25
|
6
|
+
Metrics/MethodLength:
|
7
|
+
Max: 25
|
8
|
+
# Set parameter number to 10
|
9
|
+
Metrics/ParameterLists:
|
10
|
+
Max: 10
|
11
|
+
# Disable checking of ABC metrics (assignment, branch, condition)
|
12
|
+
Metrics/AbcSize:
|
13
|
+
Enabled: false
|
14
|
+
# Disable method perceived complexity (how it is hard to read a method)
|
15
|
+
Metrics/PerceivedComplexity:
|
16
|
+
Enabled: false
|
17
|
+
# Disable cyclomatic complexity which is a number of paths through a method
|
18
|
+
Metrics/CyclomaticComplexity:
|
19
|
+
Enabled: false
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -64,6 +64,10 @@ Options:
|
|
64
64
|
-c, [--css=CSS] # Specify a CSS file path for Hidive subs
|
65
65
|
-l, [--line-offset=N] # Specify a line offset for the main dialog (e.g. 50 lowers the text line by 50px of the total height)
|
66
66
|
# Default: 0
|
67
|
+
-w, [--width=N] # Specify the video width
|
68
|
+
# Default: 1920
|
69
|
+
-h, [--height=N] # Specify the video height
|
70
|
+
# Default: 1080
|
67
71
|
-q, [--quiet], [--no-quiet] # Don't output to the console
|
68
72
|
|
69
73
|
Run the VTT to ASS conversion for the specified file(s)
|
@@ -99,7 +103,7 @@ $ gem build vtt2ass.gemspec
|
|
99
103
|
|
100
104
|
To install the gem file:
|
101
105
|
```bash
|
102
|
-
$ gem install ./vtt2ass-0.3.
|
106
|
+
$ gem install ./vtt2ass-0.3.5.gem
|
103
107
|
```
|
104
108
|
|
105
109
|
## License
|
data/Rakefile
CHANGED
@@ -1,10 +1,12 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
require 'rake/testtask'
|
3
5
|
|
4
6
|
Rake::TestTask.new(:test) do |t|
|
5
|
-
t.libs <<
|
6
|
-
t.libs <<
|
7
|
-
t.test_files = FileList[
|
7
|
+
t.libs << 'test'
|
8
|
+
t.libs << 'lib'
|
9
|
+
t.test_files = FileList['test/**/*_test.rb']
|
8
10
|
end
|
9
11
|
|
10
|
-
task :
|
12
|
+
task default: :test
|
data/bin/vtt2ass
CHANGED
data/exe/vtt2ass
CHANGED
data/lib/vtt2ass/application.rb
CHANGED
@@ -1,77 +1,81 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require_relative 'vtt_file'
|
3
4
|
require_relative 'ass_file'
|
4
5
|
|
5
6
|
##
|
6
7
|
# Main application class that manages all the operations.
|
7
8
|
class Application
|
9
|
+
##
|
10
|
+
# Creates a new Application instance.
|
11
|
+
# It receives +options+ that can define the input and output directories.
|
12
|
+
def initialize(input, options)
|
13
|
+
@options = options
|
14
|
+
@input = sanitize_path(input)
|
15
|
+
end
|
8
16
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
@width = 1920
|
16
|
-
@height = 1080
|
17
|
-
@font_family = options[:font_family] ? options[:font_family] : 'Open Sans Semibold'
|
18
|
-
@font_size = options[:font_size] ? options[:font_size] : 52
|
19
|
-
if options[:title] then
|
20
|
-
@title = options[:title]
|
21
|
-
end
|
22
|
-
@quiet = options[:quiet]
|
23
|
-
if options[:css] then
|
24
|
-
@css = options[:css].gsub('\\', '/').delete_suffix('/')
|
25
|
-
end
|
26
|
-
@line_offset = options[:line_offset]
|
27
|
-
end
|
17
|
+
##
|
18
|
+
# Replace backslashes from Windows paths to normal slashes.
|
19
|
+
# Deletes the trailing slash if there is one.
|
20
|
+
def sanitize_path(path)
|
21
|
+
path&.gsub('\\', '/')&.delete_suffix('/')
|
22
|
+
end
|
28
23
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
end
|
24
|
+
##
|
25
|
+
# This method starts the application process.
|
26
|
+
# It sends the file_paths of VTT files in the input directory to convertFileToASS method
|
27
|
+
# and outputs the resulting ASS format to a new file.
|
28
|
+
def start
|
29
|
+
if File.directory?(@input)
|
30
|
+
Dir["#{@input}/*.vtt"].each do |file_path|
|
31
|
+
convert(file_path)
|
32
|
+
end
|
33
|
+
elsif File.file?(@input)
|
34
|
+
convert(@input)
|
35
|
+
else
|
36
|
+
raise StandardError, 'ERROR: Input file or directory does not exist.'
|
43
37
|
end
|
38
|
+
rescue SystemExit, Interrupt
|
39
|
+
puts 'ERROR: The application stopped unexpectedly. The conversion may not have been completed.'
|
40
|
+
rescue StandardError => e
|
41
|
+
puts e.message
|
42
|
+
puts e.backtrace
|
43
|
+
end
|
44
44
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
end
|
45
|
+
##
|
46
|
+
# This method launches the conversion process on the specified input file.
|
47
|
+
def convert(input_path)
|
48
|
+
output = sanitize_path(@options[:output])
|
49
|
+
output = '.' if output.nil?
|
50
|
+
raise StandardError, 'ERROR: Output directory does not exist.' unless File.directory?(output)
|
52
51
|
|
53
|
-
|
54
|
-
#
|
55
|
-
|
56
|
-
|
57
|
-
base_file_name = File.basename(file_path).gsub('.vtt', '')
|
58
|
-
css_file = nil
|
59
|
-
if defined?(@css) and File.directory?(@css) then
|
60
|
-
css_file = "#{@css}/#{base_file_name}.css"
|
61
|
-
elsif File.file?("#{file_path.gsub('.vtt', '')}.css") then
|
62
|
-
css_file = "#{file_path.gsub('.vtt', '')}.css"
|
63
|
-
else
|
64
|
-
css_file = @css
|
65
|
-
end
|
66
|
-
vtt_file = VTTFile.new(file_path, @width, @height)
|
67
|
-
ass_file = ASSFile.new(
|
68
|
-
(defined?(@title) ? @title : base_file_name),
|
69
|
-
@width,
|
70
|
-
@height,
|
71
|
-
css_file
|
72
|
-
)
|
73
|
-
ass_file.convert_vtt_to_ass(vtt_file, @font_family, @font_size, @line_offset)
|
74
|
-
return ass_file
|
75
|
-
end
|
52
|
+
ass_file = vtt_to_ass(input_path)
|
53
|
+
ass_file.write_to_file("#{output}/#{File.basename(input_path).gsub('.vtt', '.ass')}") unless output.nil?
|
54
|
+
puts ass_file.to_s unless @options[:quiet]
|
55
|
+
end
|
76
56
|
|
77
|
-
|
57
|
+
##
|
58
|
+
# This method creates a new VTTFile object from the file path provided and convert its content
|
59
|
+
# inside a new ASSFile object.
|
60
|
+
def vtt_to_ass(file_path)
|
61
|
+
base_file_name = File.basename(file_path).gsub('.vtt', '')
|
62
|
+
css_file =
|
63
|
+
if !@options[:css].nil? && File.directory?(@options[:css])
|
64
|
+
"#{sanitize_path(@options[:css])}/#{base_file_name}.css"
|
65
|
+
elsif File.file?("#{file_path.gsub('.vtt', '')}.css")
|
66
|
+
"#{file_path.gsub('.vtt', '')}.css"
|
67
|
+
else
|
68
|
+
@options[:css]
|
69
|
+
end
|
70
|
+
vtt_file = VTTFile.new(file_path, @options[:width], @options[:height])
|
71
|
+
ass_file = ASSFile.new(
|
72
|
+
(@options[:title].nil? ? base_file_name : @options[:title]),
|
73
|
+
@options[:width],
|
74
|
+
@options[:height],
|
75
|
+
css_file
|
76
|
+
)
|
77
|
+
ass_file.convert_vtt_to_ass(vtt_file, @options[:font_family], @options[:font_size],
|
78
|
+
{ line: @options[:line_offset], caption: @options[:caption_offset] })
|
79
|
+
ass_file
|
80
|
+
end
|
81
|
+
end
|
data/lib/vtt2ass/ass_file.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require_relative 'ass_line'
|
3
4
|
require_relative 'ass_style'
|
4
5
|
require_relative 'css_file'
|
@@ -7,104 +8,100 @@ require_relative 'css_rule'
|
|
7
8
|
##
|
8
9
|
# This class defines an ASS subtitle file.
|
9
10
|
class ASSFile
|
10
|
-
|
11
|
-
|
11
|
+
attr_reader :title, :width, :height
|
12
|
+
attr_accessor :ass_styles, :ass_lines
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
@events = [
|
36
|
-
'',
|
37
|
-
'[Events]',
|
38
|
-
'Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text'
|
39
|
-
]
|
40
|
-
@ass_styles = []
|
41
|
-
@ass_lines = []
|
42
|
-
end
|
14
|
+
##
|
15
|
+
# Creates a new ASSFile instance and assigns the default values of instance variables.
|
16
|
+
def initialize(title, width, height, css_file_path = nil)
|
17
|
+
@width = width
|
18
|
+
@height = height
|
19
|
+
@css_file = nil
|
20
|
+
@css_file = CSSFile.new(css_file_path) unless css_file_path.nil?
|
21
|
+
@header = <<~HEADER
|
22
|
+
[Script Info]
|
23
|
+
Title: #{title}
|
24
|
+
ScriptType: v4.00+
|
25
|
+
Collisions: Normal
|
26
|
+
PlayDepth: 0
|
27
|
+
PlayResX: #{@width}
|
28
|
+
PlayResY: #{@height}
|
29
|
+
WrapStyle: 0
|
30
|
+
ScaledBorderAndShadow: yes
|
31
|
+
|
32
|
+
[V4+ Styles]
|
33
|
+
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
|
34
|
+
HEADER
|
35
|
+
@events = <<~EVENTS
|
43
36
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
case property[:key]
|
68
|
-
when 'font-family'
|
69
|
-
font_family = property[:value].gsub('"', '').split(' ,').last
|
70
|
-
when 'font-size'
|
71
|
-
em_size = 1
|
72
|
-
#em_size = property[:value][0].eql? '.' ? "0#{property[:value]}" : property[:value]
|
73
|
-
if property[:value][0].eql? '.' then
|
74
|
-
em_size = "0#{property[:value]}".gsub('em', '').to_f
|
75
|
-
end
|
76
|
-
font_size = (fs * em_size).to_i
|
77
|
-
when 'color'
|
78
|
-
font_color = ASSStyle.convert_color(property[:value])
|
79
|
-
when 'font-weight'
|
80
|
-
if property[:value].eql? 'bold' then
|
81
|
-
is_bold = true
|
82
|
-
end
|
83
|
-
when 'font-style'
|
84
|
-
if property[:value].eql? 'italic' then
|
85
|
-
is_italic = true
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
@ass_styles.push(ASSStyle.new(line.style, line.params, font_family, font_size, font_color, is_bold, is_italic, line_offset, @width, @height))
|
92
|
-
end
|
37
|
+
[Events]
|
38
|
+
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
|
39
|
+
EVENTS
|
40
|
+
@ass_styles = []
|
41
|
+
@ass_lines = []
|
42
|
+
end
|
43
|
+
|
44
|
+
##
|
45
|
+
# This method receives a VTTFile object and font arguments creates new ASSLine with the params of
|
46
|
+
# each VTTLine. All those ASSLine are stored in an array. It also creates an array of ASSStyle that
|
47
|
+
# will be used in the ASS style list.
|
48
|
+
def convert_vtt_to_ass(vtt_file, font_family, font_size, offset = { line: 0, caption: 0 }) # rubocop:disable Metrics/MethodLength
|
49
|
+
fs = font_size
|
50
|
+
vtt_file.lines.each do |line| # rubocop:disable Metrics/BlockLength
|
51
|
+
font_color = '&H00FFFFFF'
|
52
|
+
is_italic = false
|
53
|
+
is_bold = false
|
54
|
+
@ass_lines.push(ASSLine.new(line.style, line.time_start, line.time_end, line.text))
|
55
|
+
style_exists = false
|
56
|
+
@ass_styles.each do |style|
|
57
|
+
if style.style_name.eql? line.style
|
58
|
+
style_exists = true
|
59
|
+
break
|
93
60
|
end
|
94
|
-
|
61
|
+
end
|
62
|
+
next if style_exists
|
95
63
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
64
|
+
unless @css_file.nil?
|
65
|
+
css_rule = @css_file.find_rule(line.style)
|
66
|
+
css_rule&.properties&.each do |property|
|
67
|
+
case property[:key]
|
68
|
+
when 'font-family'
|
69
|
+
font_family = property[:value].gsub('"', '').split(' ,').last
|
70
|
+
when 'font-size'
|
71
|
+
em_size = 1
|
72
|
+
em_size = "0#{property[:value]}".gsub('em', '').to_f if property[:value][0].eql? '.'
|
73
|
+
font_size = (fs * em_size).to_i
|
74
|
+
when 'color'
|
75
|
+
font_color = ASSStyle.convert_color(property[:value])
|
76
|
+
when 'font-weight'
|
77
|
+
is_bold = true if property[:value].eql? 'bold'
|
78
|
+
when 'font-style'
|
79
|
+
is_italic = true if property[:value].eql? 'italic'
|
80
|
+
end
|
102
81
|
end
|
82
|
+
end
|
83
|
+
@ass_styles.push(
|
84
|
+
ASSStyle.new(
|
85
|
+
line.style, line.params,
|
86
|
+
font_family, font_size, font_color, is_bold, is_italic,
|
87
|
+
offset, @width, @height
|
88
|
+
)
|
89
|
+
)
|
103
90
|
end
|
91
|
+
end
|
104
92
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
93
|
+
##
|
94
|
+
# This method writes the content of the ASSFile object into a file path that is provided.
|
95
|
+
def write_to_file(file_path)
|
96
|
+
File.open(file_path, 'w') do |line|
|
97
|
+
line.print "\ufeff"
|
98
|
+
line.puts to_s
|
109
99
|
end
|
110
|
-
end
|
100
|
+
end
|
101
|
+
|
102
|
+
##
|
103
|
+
# This method concatenates the object data in the right order for a string output.
|
104
|
+
def to_s
|
105
|
+
"#{@header}#{@ass_styles.join("\n")}#{@events}#{@ass_lines.join("\n")}"
|
106
|
+
end
|
107
|
+
end
|
data/lib/vtt2ass/ass_line.rb
CHANGED
@@ -1,94 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'htmlentities'
|
2
4
|
|
3
5
|
##
|
4
6
|
# This class defines an ASS subtile line.
|
5
7
|
class ASSLine
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
8
|
+
attr_reader :style, :time_start, :time_end, :text
|
9
|
+
|
10
|
+
##
|
11
|
+
# This method creates an instance of an ASSLine.
|
12
|
+
#
|
13
|
+
# * Requires a +style+ name as input.
|
14
|
+
# * Requires +time_start+, a VTT formatted timestamp as input.
|
15
|
+
# * Requires +time_start+, a VTT formatted timestamp as input.
|
16
|
+
# * Requires +text+, a VTT formatted string as input.
|
17
|
+
def initialize(style, time_start, time_end, text)
|
18
|
+
@style = style
|
19
|
+
@time_start = convert_time(time_start)
|
20
|
+
@time_end = convert_time(time_end)
|
21
|
+
@text = convert_to_ass_text(text)
|
22
|
+
end
|
23
|
+
|
24
|
+
##
|
25
|
+
# This method assigns the object values and outputs an ASS dialogue line.
|
26
|
+
def to_s
|
27
|
+
"Dialogue: 0,#{@time_start},#{@time_end},#{@style},,0,0,0,,#{@text}"
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# This method replaces characters and tags to ASS compatible characters and tags.
|
32
|
+
#
|
33
|
+
# * Requires +text+, a string of VTT formated text as input.
|
34
|
+
def convert_to_ass_text(text)
|
35
|
+
decoder = HTMLEntities.new
|
36
|
+
text =
|
37
|
+
text
|
38
|
+
.gsub(/\r/, '')
|
39
|
+
.gsub(/\n/, '\\N')
|
40
|
+
.gsub(/\\n/, '\\N')
|
41
|
+
.gsub(/\\N +/, '\\N')
|
42
|
+
.gsub(/ +\\N/, '\\N')
|
43
|
+
.gsub(/(\\N)+/, '\\N')
|
44
|
+
.gsub(%r{<b[^>]*>([^<]*)</b>}) { |_s| "{\\b1}#{Regexp.last_match(1)}{\\b0}" }
|
45
|
+
.gsub(%r{<i[^>]*>([^<]*)</i>}) { |_s| "{\\i1}#{Regexp.last_match(1)}{\\i0}" }
|
46
|
+
.gsub(%r{<u[^>]*>([^<]*)</u>}) { |_s| "{\\u1}#{Regexp.last_match(1)}{\\u0}" }
|
47
|
+
.gsub(%r{<c[^>]*>([^<]*)</c>}) { |_s| Regexp.last_match(1) }
|
48
|
+
.gsub(/<[^>]>/, '')
|
49
|
+
.gsub(/\\N$/, '')
|
50
|
+
.gsub(/ +$/, '')
|
51
|
+
decoder.decode(text)
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
# This method validates the time format and sends the matching time to be converted
|
56
|
+
#
|
57
|
+
# * Requires +str+, a VTT formatted time string.
|
58
|
+
def convert_time(time)
|
59
|
+
matched_time = time.match(/([\d:]*)\.?(\d*)/)
|
60
|
+
to_subs_time(matched_time[0])
|
61
|
+
end
|
62
|
+
|
63
|
+
##
|
64
|
+
# This method converts time from VTT format to the ASS format.
|
65
|
+
#
|
66
|
+
# * Requires +str+, a VTT formatted time string.
|
67
|
+
def to_subs_time(str)
|
68
|
+
n = []
|
69
|
+
x = str.split(/[:.]/).map(&:to_i)
|
70
|
+
|
71
|
+
ms_len = 2
|
72
|
+
h_len = 1
|
73
|
+
|
74
|
+
x[3] = "0.#{x[3].to_s.rjust(3, '0')}"
|
75
|
+
sx = (x[0] * 60 * 60) + (x[1] * 60) + x[2] + x[3].to_f
|
76
|
+
sx = format('%.2f', sx).split('.')
|
77
|
+
|
78
|
+
n.unshift(pad_time_num('.', sx[1], ms_len))
|
79
|
+
sx = sx[0].to_f
|
21
80
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
return "Dialogue: 0,#{@time_start},#{@time_end},#{@style},,0,0,0,,#{@text}"
|
26
|
-
end
|
81
|
+
n.unshift(pad_time_num(':', (sx % 60).to_i, 2))
|
82
|
+
n.unshift(pad_time_num(':', (sx / 60).floor % 60, 2))
|
83
|
+
n.unshift(pad_time_num('', (sx / 3600).floor % 60, h_len))
|
27
84
|
|
28
|
-
|
29
|
-
|
30
|
-
#
|
31
|
-
# * Requires +text+, a string of VTT formated text as input.
|
32
|
-
def convert_to_ass_text(text)
|
33
|
-
decoder = HTMLEntities.new()
|
34
|
-
text = text
|
35
|
-
.gsub(/\r/, '')
|
36
|
-
.gsub(/\n/, '\\N')
|
37
|
-
.gsub(/\\n/, '\\N')
|
38
|
-
.gsub(/\\N +/, '\\N')
|
39
|
-
.gsub(/ +\\N/, '\\N')
|
40
|
-
.gsub(/(\\N)+/, '\\N')
|
41
|
-
.gsub(/<b[^>]*>([^<]*)<\/b>/) { |s| "{\\b1}#{$1}{\\b0}" }
|
42
|
-
.gsub(/<i[^>]*>([^<]*)<\/i>/) { |s| "{\\i1}#{$1}{\\i0}" }
|
43
|
-
.gsub(/<u[^>]*>([^<]*)<\/u>/) { |s| "{\\u1}#{$1}{\\u0}" }
|
44
|
-
.gsub(/<c[^>]*>([^<]*)<\/c>/) { |s| $1 }
|
45
|
-
.gsub(/<[^>]>/, '')
|
46
|
-
.gsub(/\\N$/, '')
|
47
|
-
.gsub(/ +$/, '')
|
48
|
-
return decoder.decode(text)
|
49
|
-
end
|
85
|
+
n.join
|
86
|
+
end
|
50
87
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
# This method converts time from VTT format to the ASS format.
|
62
|
-
#
|
63
|
-
# * Requires +str+, a VTT formatted time string.
|
64
|
-
def to_subs_time(str)
|
65
|
-
n = []
|
66
|
-
x = str.split(/[:.]/).map { |x| x.to_i }
|
67
|
-
|
68
|
-
msLen = 2
|
69
|
-
hLen = 1
|
70
|
-
|
71
|
-
x[3] = '0.' + (x[3].to_s).rjust(3, '0')
|
72
|
-
sx = x[0]*60*60 + x[1]*60 + x[2] + x[3].to_f
|
73
|
-
sx = ("%.2f" % sx).split('.')
|
74
|
-
|
75
|
-
n.unshift(pad_time_num('.', sx[1], msLen))
|
76
|
-
sx = sx[0].to_f
|
77
|
-
|
78
|
-
n.unshift(pad_time_num(':', (sx % 60).to_i, 2))
|
79
|
-
n.unshift(pad_time_num(':', (sx / 60).floor % 60, 2))
|
80
|
-
n.unshift(pad_time_num('', (sx / 3600).floor % 60, hLen))
|
81
|
-
|
82
|
-
return n.join('')
|
83
|
-
end
|
84
|
-
|
85
|
-
##
|
86
|
-
# This method pads text so that time numbers are a fixed number of digit.
|
87
|
-
#
|
88
|
-
# * Requires +sep+, a string separator.
|
89
|
-
# * Requires +input+, an integer.
|
90
|
-
# * Requires +pad+, an integer for the number of digits to be padded.
|
91
|
-
def pad_time_num(sep, input, pad)
|
92
|
-
return sep + (input.to_s).rjust(pad, '0')
|
93
|
-
end
|
94
|
-
end
|
88
|
+
##
|
89
|
+
# This method pads text so that time numbers are a fixed number of digit.
|
90
|
+
#
|
91
|
+
# * Requires +sep+, a string separator.
|
92
|
+
# * Requires +input+, an integer.
|
93
|
+
# * Requires +pad+, an integer for the number of digits to be padded.
|
94
|
+
def pad_time_num(sep, input, pad)
|
95
|
+
sep + input.to_s.rjust(pad, '0')
|
96
|
+
end
|
97
|
+
end
|