vtt2ass 0.3.2 → 0.3.5

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5e94988be1922e26d861f105404e3eff863a2df470aa3aa7fc5f35cdf26c3c71
4
- data.tar.gz: 055641cfd88eef119e3c623adbae16b9eb70a7091c26b13410d8b94c2c8e55ec
3
+ metadata.gz: 3047227acbd6bf731e5dc95113a7d70081bed94392e3b00078b760ebbc4440a4
4
+ data.tar.gz: e9bc514e5b41028d872d9e983cc4dbd4442fc648ab1065e2c3e5a2aca3c9b8fd
5
5
  SHA512:
6
- metadata.gz: 785df255fc71e0479a8b124222e25033e2aca337765cdd17bf46c6ef0e11adae7fb854b01fa23959946f8d9ebd8bbfefcaf6cafc10b9a7089a52b11806f5e82a
7
- data.tar.gz: 9cd4940b1d5667136aa51d9bff670a2d98e4a966ee898110230c15d710c87898aaf596afd6a6c7173f0ef06cf26eb57e7f2b3acd1c670f95209d97be9c5d5480
6
+ metadata.gz: 388af342ebe22e69e52e92e4dadf5a04ea48cb6e491398e864ed1e9d5ae3a602601cddc070ae20f7e8edef0b652a87aa3e8a66f8b8128626315dbac0dd421873
7
+ data.tar.gz: fa2f3b0fa33c3ee1ade343b14837e8f09d2b5145f41e60f68e1cc7236e17e23c67cb7a811f5abf145df7e7121fd651364870073b6ff74679f6c9ddfe375a2ddc
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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  # Specify your gem's dependencies in vtt2ass.gemspec
data/README.md CHANGED
@@ -2,35 +2,54 @@
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/vtt2ass.svg)](https://badge.fury.io/rb/vtt2ass)
4
4
 
5
- This is a simple application to convert VTT files to ASS subtitles.
5
+ ## Description
6
+
7
+ This is a simple CLI (Command Line Interface) application to convert VTT files to ASS subtitles.
8
+
9
+ This application is originally based on the `vttconvert` module of [anidl/hidive-downloader-nx](https://github.com/anidl/hidive-downloader-nx) repository. The [maxwbot/maxwhidive](https://github.com/maxwbot/maxwhidive) repository was also used as inspiration for handling the positionning of subs.
10
+
11
+ Those two tools were missing features and didn't work well on a lot of more complex subtitles files. For that reason, I wrote a new tool that can handle everything.
12
+
13
+ ### Features
14
+
15
+ - Convert simple VTT files
16
+ - Convert complex VTT files with positioning
17
+ - Convert Hidive VTT files with CSS styling
18
+ - Convert subtitles in batches by specifying the input directory
19
+ - Handles subtitles made for lower resolution video
20
+ - Can add offset to subtitle lines
21
+ - Can output result to the CLI
22
+ - Can output to the specifed directory
23
+ - Can change the base font size
24
+ - Can specify a custom font family for non-styled lines
25
+ - Can add a title to the converted files
6
26
 
7
27
  ## Requirements
28
+
8
29
  - ruby 2.7.2 or newer
9
30
 
10
- It might work with older versions of ruby, but it hasn't been tested
31
+ Development is currently done on ruby 3.0+, but the Gitlab runner for builds works with ruby version 2.7.2. Older versions of ruby may be compatible, but they won't be tested.
11
32
 
12
33
  ## Installation
13
34
 
14
35
  To install:
15
36
  ```bash
16
- gem install vtt2ass
17
- ```
18
-
19
- # Build
20
-
21
- ```bash
22
- gem build vtt2ass.gemspec
37
+ $ gem install vtt2ass
23
38
  ```
24
39
 
25
40
  ## Usage
26
41
 
42
+ - Empty arguments lists the available commands
27
43
  ```bash
28
44
  $ vtt2ass
29
45
  Commands:
30
46
  vtt2ass convert INPUT # Run the VTT to ASS conversion for the specified file(s)
31
47
  vtt2ass help [COMMAND] # Describe available commands or one specific command
32
48
  vtt2ass version # Show version
49
+ ```
33
50
 
51
+ - Help command shows available options of the specified command
52
+ ```bash
34
53
  $ vtt2ass help convert
35
54
  Usage:
36
55
  vtt2ass convert INPUT
@@ -45,12 +64,53 @@ Options:
45
64
  -c, [--css=CSS] # Specify a CSS file path for Hidive subs
46
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)
47
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
48
71
  -q, [--quiet], [--no-quiet] # Don't output to the console
49
72
 
50
73
  Run the VTT to ASS conversion for the specified file(s)
51
74
  ```
52
75
 
53
- # Donate
76
+ - Convert command
77
+ ```bash
78
+ $ vtt2ass convert ./path/to/input/ -o ./path/to/output/ -l 50 -q
79
+ ```
80
+
81
+ - Version command shows the application version
82
+ ```bash
83
+ $ vtt2ass version
84
+ 0.3.3
85
+ ```
86
+
87
+ ## Contributing
88
+
89
+ Contributions are welcome. Create an *Issue* on Gitlab and link it with a *Pull Request* of the changes made. The changes needs to pass the ruby tests.
90
+
91
+ ```
92
+ $ rake test
93
+ ```
94
+
95
+ ## Build
96
+
97
+ To build a gem file for local installation:
98
+ ```bash
99
+ $ git clone https://gitlab.com/dkb-weeblets/vtt2ass.git
100
+ $ cd vtt2ass/
101
+ $ gem build vtt2ass.gemspec
102
+ ```
103
+
104
+ To install the gem file:
105
+ ```bash
106
+ $ gem install ./vtt2ass-0.3.5.gem
107
+ ```
108
+
109
+ ## License
110
+
111
+ Licensed under the **MIT** Licence. For more information read the `LICENSE.txt` file.
112
+
113
+ ## Donate
54
114
 
55
115
  If you want to support me, consider buying me a coffee.
56
116
 
data/Rakefile CHANGED
@@ -1,10 +1,12 @@
1
- require "bundler/gem_tasks"
2
- require "rake/testtask"
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 << "test"
6
- t.libs << "lib"
7
- t.test_files = FileList["test/**/*_test.rb"]
7
+ t.libs << 'test'
8
+ t.libs << 'lib'
9
+ t.test_files = FileList['test/**/*_test.rb']
8
10
  end
9
11
 
10
- task :default => :test
12
+ task default: :test
data/bin/vtt2ass CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require_relative '../lib/vtt2ass'
4
- MainCommand.start
5
+ MainCommand.start
data/exe/vtt2ass CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'vtt2ass'
4
- MainCommand.start
5
+ MainCommand.start
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'vtt_file'
4
+ require_relative 'ass_file'
5
+
6
+ ##
7
+ # Main application class that manages all the operations.
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
16
+
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
23
+
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.'
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
+ end
43
+
44
+ ##
45
+ # This method launches the conversion process on the specified input file.
46
+ def convert(input_path)
47
+ output = sanitize_path(@options[:output])
48
+ raise StandardError, 'ERROR: Output directory does not exist.' unless File.directory?(output)
49
+
50
+ ass_file = vtt_to_ass(input_path)
51
+ ass_file.write_to_file("#{output}/#{File.basename(input_path).gsub('.vtt', '.ass')}") unless output.nil?
52
+ puts ass_file.to_s unless @options[:quiet]
53
+ end
54
+
55
+ ##
56
+ # This method creates a new VTTFile object from the file path provided and convert its content
57
+ # inside a new ASSFile object.
58
+ def vtt_to_ass(file_path)
59
+ base_file_name = File.basename(file_path).gsub('.vtt', '')
60
+ css_file =
61
+ if !@options[:css].nil? && File.directory?(@options[:css])
62
+ "#{sanitize_path(@options[:css])}/#{base_file_name}.css"
63
+ elsif File.file?("#{file_path.gsub('.vtt', '')}.css")
64
+ "#{file_path.gsub('.vtt', '')}.css"
65
+ else
66
+ @options[:css]
67
+ end
68
+ vtt_file = VTTFile.new(file_path, @options[:width], @options[:height])
69
+ ass_file = ASSFile.new(
70
+ (@options[:title].nil? ? base_file_name : @options[:title]),
71
+ @options[:width],
72
+ @options[:height],
73
+ css_file
74
+ )
75
+ ass_file.convert_vtt_to_ass(vtt_file, @options[:font_family], @options[:font_size], @options[:line_offset])
76
+ ass_file
77
+ end
78
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'ass_line'
4
+ require_relative 'ass_style'
5
+ require_relative 'css_file'
6
+ require_relative 'css_rule'
7
+
8
+ ##
9
+ # This class defines an ASS subtitle file.
10
+ class ASSFile
11
+ attr_reader :title, :width, :height
12
+ attr_accessor :ass_styles, :ass_lines
13
+
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 = CSSFile.new(css_file_path) unless css_file_path.nil?
20
+ @header = <<~HEADER
21
+ [Script Info]
22
+ Title: #{title}
23
+ ScriptType: v4.00+
24
+ Collisions: Normal
25
+ PlayDepth: 0
26
+ PlayResX: #{@width}
27
+ PlayResY: #{@height}
28
+ WrapStyle: 0
29
+ ScaledBorderAndShadow: yes
30
+
31
+ [V4+ Styles]
32
+ Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
33
+ HEADER
34
+ @events = <<~EVENTS
35
+
36
+ [Events]
37
+ Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
38
+ EVENTS
39
+ @ass_styles = []
40
+ @ass_lines = []
41
+ end
42
+
43
+ ##
44
+ # This method receives a VTTFile object and font arguments creates new ASSLine with the params of
45
+ # each VTTLine. All those ASSLine are stored in an array. It also creates an array of ASSStyle that
46
+ # will be used in the ASS style list.
47
+ def convert_vtt_to_ass(vtt_file, font_family, font_size, line_offset = 0) # rubocop:disable Metrics/MethodLength
48
+ fs = font_size
49
+ vtt_file.lines.each do |line| # rubocop:disable Metrics/BlockLength
50
+ font_color = '&H00FFFFFF'
51
+ is_italic = false
52
+ is_bold = false
53
+ @ass_lines.push(ASSLine.new(line.style, line.time_start, line.time_end, line.text))
54
+ style_exists = false
55
+ @ass_styles.each do |style|
56
+ if style.style_name.eql? line.style
57
+ style_exists = true
58
+ break
59
+ end
60
+ end
61
+ next if style_exists
62
+
63
+ unless @css_file.nil?
64
+ css_rule = @css_file.find_rule(line.style)
65
+ css_rule&.properties&.each do |property|
66
+ case property[:key]
67
+ when 'font-family'
68
+ font_family = property[:value].gsub('"', '').split(' ,').last
69
+ when 'font-size'
70
+ em_size = 1
71
+ em_size = "0#{property[:value]}".gsub('em', '').to_f if property[:value][0].eql? '.'
72
+ font_size = (fs * em_size).to_i
73
+ when 'color'
74
+ font_color = ASSStyle.convert_color(property[:value])
75
+ when 'font-weight'
76
+ is_bold = true if property[:value].eql? 'bold'
77
+ when 'font-style'
78
+ is_italic = true if property[:value].eql? 'italic'
79
+ end
80
+ end
81
+ end
82
+ @ass_styles.push(
83
+ ASSStyle.new(
84
+ line.style, line.params,
85
+ font_family, font_size, font_color, is_bold, is_italic,
86
+ line_offset, @width, @height
87
+ )
88
+ )
89
+ end
90
+ end
91
+
92
+ ##
93
+ # This method writes the content of the ASSFile object into a file path that is provided.
94
+ def write_to_file(file_path)
95
+ File.open(file_path, 'w') do |line|
96
+ line.print "\ufeff"
97
+ line.puts to_s
98
+ end
99
+ end
100
+
101
+ ##
102
+ # This method concatenates the object data in the right order for a string output.
103
+ def to_s
104
+ "#{@header}#{@ass_styles.join("\n")}#{@events}#{@ass_lines.join("\n")}"
105
+ end
106
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'htmlentities'
4
+
5
+ ##
6
+ # This class defines an ASS subtile line.
7
+ class ASSLine
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
80
+
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))
84
+
85
+ n.join
86
+ end
87
+
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
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'ass_style_params'
4
+ require_relative 'validator'
5
+ require 'redgreenblue'
6
+
7
+ ##
8
+ # This class defines an ASS style that can be applied on a subtitle line.
9
+ class ASSStyle
10
+ attr_reader :style_name
11
+
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, line_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 -= line_offset if style_name.include? 'Subtitle'
29
+ @is_italic = is_italic
30
+ @is_bold = is_bold
31
+ end
32
+
33
+ ##
34
+ # This method assigns the object values to an ASS style line and outputs it.
35
+ def to_s
36
+ # Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour,
37
+ # Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment,
38
+ # MarginL, MarginR, MarginV, Encoding
39
+ "Style: #{@style_name},#{@font_family},#{@font_size},#{@font_color},&H000000FF,&H00020713,&H00000000,"\
40
+ "#{@is_bold ? '-1' : '0'},#{@is_italic ? '-1' : '0'},0,0,100,100,0,0,1,2.0,2.0,#{@s_params.alignment},"\
41
+ "#{@s_params.horizontal_margin},0,#{@s_params.vertical_margin},1"
42
+ end
43
+
44
+ ##
45
+ # This method returns a ASS formated color value based on hex or color name value
46
+ def self.convert_color(color_value)
47
+ color_value.gsub!('#', '')
48
+ color = Validator.hex?(color_value) ? RGB.hex(color_value) : RGB.css(color_value)
49
+ format('&H00%<blue>02x%<green>02x%<red>02x', blue: color.b, green: color.g, red: color.r).upcase
50
+ end
51
+ end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ ##
4
+ # This class defines the ASS style parameters from VTT cue settings.
5
+ class ASSStyleParams
6
+ attr_accessor :horizontal_margin, :vertical_margin, :alignment, :align
7
+
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
+ split_params(params)
14
+ create_alignment
15
+ create_horizontal_margin(width)
16
+ create_vertical_margin(height)
17
+ end
18
+
19
+ def split_params(params)
20
+ (params.split.map { |p| p.split(':') }).each do |p|
21
+ case p[0]
22
+ when 'position'
23
+ @position = p[1].gsub(/%/, '').to_i
24
+ when 'line'
25
+ @line = p[1].gsub(/%/, '').to_i
26
+ @line = @line == -1 ? 100 : @line
27
+ when 'align'
28
+ @align = p[1].chomp
29
+ end
30
+ end
31
+ end
32
+
33
+ ##
34
+ # This method decides the alignement value in a 9 position grid based of the
35
+ # values in cue settings "align" and "line".
36
+ def create_alignment
37
+ @alignment =
38
+ if defined?(@line) && !defined?(@position)
39
+ find_alignment(@align)
40
+ elsif defined?(@line) && defined?(@position)
41
+ 1 # bottom left
42
+ else
43
+ find_default_alignment(@align)
44
+ end
45
+ end
46
+
47
+ ##
48
+ # This method returns alignment when "line" value is specified but not "position"
49
+ def find_alignment(align)
50
+ if align.nil?
51
+ # If position is higher than 50% align to bottom center, else align to top center
52
+ @line >= 50 ? 2 : 8
53
+ else
54
+ case align
55
+ when 'left', 'start'
56
+ @line >= 50 ? 1 : 7
57
+ when 'right', 'end'
58
+ @line >= 50 ? 3 : 9
59
+ when 'center', 'middle'
60
+ @line >= 50 ? 2 : 8
61
+ end
62
+ end
63
+ end
64
+
65
+ ##
66
+ # This method returns alignment when "line" and "position" values are not specified
67
+ def find_default_alignment(align)
68
+ case align
69
+ when 'left', 'start'
70
+ 1
71
+ when 'right', 'end'
72
+ 3
73
+ # when 'center', 'middle'
74
+ else
75
+ 2
76
+ end
77
+ end
78
+
79
+ ##
80
+ # This method calculates the horizontal margin in px between the alignement position and
81
+ # and the content displayed by using the "position" cue setting.
82
+ def create_horizontal_margin(width)
83
+ steps = (width / 100).to_i
84
+ @horizontal_margin =
85
+ if defined?(@position)
86
+ @position * steps
87
+ else
88
+ 0
89
+ end
90
+ end
91
+
92
+ ##
93
+ # This method calculates the vertical margin in px between the alignement position and
94
+ # and the content displayed by using the "line" cue setting.
95
+ def create_vertical_margin(height)
96
+ steps = (height / 100).to_i
97
+ @vertical_margin =
98
+ if defined?(@line)
99
+ if @alignment == 1
100
+ (100 - @line) * steps
101
+ else
102
+ @line >= 50 ? (100 - @line) * steps : @line * steps
103
+ end
104
+ else
105
+ 50
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'css_parser'
4
+ require_relative 'css_rule'
5
+
6
+ ##
7
+ # This class defines a CSS file for subtitles.
8
+ class CSSFile
9
+ attr_reader :rules
10
+
11
+ include CssParser
12
+
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?
21
+ end
22
+ end
23
+
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
31
+ end
32
+ return_rule
33
+ end
34
+
35
+ def to_s
36
+ @file_path
37
+ end
38
+ end