captive 1.0.0 → 1.1.0

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: bf5006d6ac1443e871a8d081974beaa6eabbf72546a25e2fa7aadefd6b9c50f7
4
- data.tar.gz: 0f4eadcf1a9cb07883cdf46f79ad371095339b8e2182ae842fbe0ffb1c833a47
3
+ metadata.gz: 79a71dcf3548d49781cc44f2d7141d53db3268b22e535ce6d3871a560b2ff416
4
+ data.tar.gz: b5e44c915d7c0d16e0cf24a62c053f0526f765c5f7a6aff061e2bbd2666fa848
5
5
  SHA512:
6
- metadata.gz: a3e7f5db587122c189bf7253e02ed79b0e4df13039aa3689d1b0a8c9062b16308bae85d5480b2b1574fb105688274c5dc0abaf8864416a14e8886ac02b4c36f2
7
- data.tar.gz: 6622b6df7e11103bea6a511b2cf53e918023955e6e6fbb1fa3cbb7d6f81373f4896cb6504754eeaf0bff717f5608f64360dc050ae1f0e750c0aa6577da4a9c85
6
+ metadata.gz: d407c301e9d7f617bd99fae21c3b45dfd0753a11afb1993a1be500e9f95fab1cdcc8606aef69c826c3cf7c5c981df600933773cb30c9f36f5de84dea0453d6c2
7
+ data.tar.gz: 36906e52e3acb139114be27ba7ffd35dd2a0f494580325b10c1fbec3f271b741da32f277ef607080e531449c9c02f6203c61df13e161f73fa0deccca99b8cb1a
@@ -14,6 +14,14 @@ module Captive
14
14
  def from_blob(blob:)
15
15
  new(cue_list: parse(blob: blob))
16
16
  end
17
+
18
+ def from_json(json:)
19
+ json = JSON.parse(json) if json.is_a?(String)
20
+ raise InvalidJsonInput unless json.key?('cues') && json['cues'].is_a?(Array)
21
+
22
+ cues = json['cues'].map { |cue_json| Cue.from_json(json: cue_json) }
23
+ new(cue_list: cues)
24
+ end
17
25
  end
18
26
 
19
27
  def self.included(base)
@@ -21,10 +29,10 @@ module Captive
21
29
  base.include(Util)
22
30
  end
23
31
 
24
- attr_accessor(:cue_list)
32
+ attr_accessor(:cues)
25
33
 
26
34
  def initialize(cue_list: nil)
27
- @cue_list = cue_list || []
35
+ @cues = cue_list || []
28
36
  end
29
37
 
30
38
  def save_as(filename:)
@@ -36,7 +44,7 @@ module Captive
36
44
  def as_json(**args)
37
45
  results = {
38
46
  'version' => VERSION,
39
- 'cues' => @cue_list.map(&:as_json),
47
+ 'cues' => cues.map(&:as_json),
40
48
  }
41
49
  if results.respond_to?(:as_json)
42
50
  results.as_json(**args)
@@ -83,7 +91,7 @@ module Captive
83
91
  if self.class.to_s.split('::').last == format
84
92
  self
85
93
  else
86
- base_klass.const_get(format).new(cue_list: cue_list)
94
+ base_klass.const_get(format).new(cue_list: cues)
87
95
  end
88
96
  end
89
97
  end
@@ -12,18 +12,35 @@ module Captive
12
12
  # List of Text Properties
13
13
  TEXT_PROPERTIES = [ALIGNMENT, COLOR, POSITION].freeze
14
14
 
15
- attr_accessor :number, :text, :properties
15
+ attr_accessor :text, :properties
16
16
  attr_reader :start_time, :end_time
17
17
 
18
18
  # Creates a new Cue class denoting a subtitle.
19
- def initialize(text: nil, start_time: nil, end_time: nil, cue_number: nil, properties: {})
19
+ def initialize(text: nil, start_time: nil, end_time: nil, properties: {})
20
20
  self.text = text
21
21
  self.start_time = start_time
22
22
  self.end_time = end_time
23
- self.number = cue_number
24
23
  self.properties = properties || {}
25
24
  end
26
25
 
26
+ def self.from_json(json:, mapping: {})
27
+ schema = {}
28
+ %i[text! start_time! end_time! properties].each do |field|
29
+ field_name = field.to_s.delete('!')
30
+ schema[field] = mapping[field_name] || mapping[field_name.to_sym] || field_name.to_sym
31
+ end
32
+ data = {}
33
+ schema.each do |mask, mapper|
34
+ key = mask[-1] == '!' ? mask.to_s[0...-1].to_sym : mask
35
+ if key.to_s != mask.to_s && !(json.key?(mapper.to_s) || json.key?(mapper.to_sym))
36
+ raise InvalidJsonInput, "Cue missing field: #{mapper}"
37
+ end
38
+
39
+ data[key] = json[mapper.to_s] || json[mapper.to_sym]
40
+ end
41
+ new(**data)
42
+ end
43
+
27
44
  def start_time=(time)
28
45
  set_time(:start_time, time)
29
46
  end
@@ -68,11 +85,15 @@ module Captive
68
85
  end
69
86
 
70
87
  def as_json(**args)
71
- if respond_to?(:instance_values) && instance_values.respond_to?(:as_json)
72
- instance_values.as_json(**args)
73
- else
74
- instance_variables.each_with_object({}) { |key, hash| hash[key[1..-1]] = instance_variable_get(key) }
75
- end
88
+ options = args.delete(:options) || {}
89
+ format = options['format'] || {}
90
+ obj = {
91
+ 'start_time' => format[:time] == :timecode ? milliseconds_to_timecode(start_time) : start_time,
92
+ 'end_time' => format[:time] == :timecode ? milliseconds_to_timecode(end_time) : end_time,
93
+ 'text' => text,
94
+ 'properties' => properties,
95
+ }
96
+ obj.respond_to?(:as_json) ? obj.as_json(**args) : obj
76
97
  end
77
98
 
78
99
  private
@@ -16,4 +16,7 @@ module Captive
16
16
  # Error denoting incorrect input to a method.
17
17
  class InvalidInput < CaptiveError
18
18
  end
19
+
20
+ class InvalidJsonInput < CaptiveError
21
+ end
19
22
  end
@@ -19,13 +19,12 @@ module Captive
19
19
 
20
20
  raise InvalidSubtitle, "Invalid Cue Number at line #{count}" if /^\d+$/.match(line).nil?
21
21
 
22
- cue = Cue.new(cue_number: line.to_i)
23
22
  state = :time
24
23
  when :time
25
24
  raise InvalidSubtitle, "Invalid Time Format at line #{count}" unless timecode?(line)
26
25
 
27
26
  start_time, end_time = line.split('-->').map(&:strip)
28
- cue.set_times(
27
+ cue = Cue.new(
29
28
  start_time: format_time(start_time),
30
29
  end_time: format_time(end_time)
31
30
  )
@@ -51,8 +50,8 @@ module Captive
51
50
 
52
51
  def to_s
53
52
  string = String.new
54
- @cue_list.each do |cue|
55
- string << cue.number.to_s
53
+ cues.each_with_index do |cue, index|
54
+ string << (index + 1).to_s
56
55
  string << "\n"
57
56
  string << milliseconds_to_timecode(cue.start_time).gsub!('.', ',')
58
57
  string << ' --> '
@@ -14,7 +14,6 @@ module Captive
14
14
  def self.parse(blob:)
15
15
  cue_list = []
16
16
  lines = blob.split("\n")
17
- cue_count = 1
18
17
  state = :new_cue
19
18
  cue = nil
20
19
  raise InvalidSubtitle, 'Invalid VTT Signature' unless validate_header(lines.shift)
@@ -31,14 +30,18 @@ module Captive
31
30
  next
32
31
  end
33
32
 
34
- # If its not metadata, and its not an empty line, it should be a timestamp
35
- raise InvalidSubtitle, "Invalid Time Format at line #{index}" unless time?(line)
33
+ # If its not metadata, and its not an empty line, it should be a timestamp or an identifier
34
+ unless time?(line)
35
+ # If this line is an identifier the next line should be a timecode
36
+ next if time?(lines[index + 1])
37
+
38
+ raise InvalidSubtitle, "Invalid Time Format at line #{index + 1}" unless time?(line)
39
+ end
36
40
 
37
41
  elements = line.split
38
42
  start_time = elements[0]
39
43
  end_time = elements[2]
40
- cue = Cue.new(cue_number: cue_count, start_time: start_time, end_time: end_time)
41
- cue_count += 1
44
+ cue = Cue.new(start_time: start_time, end_time: end_time)
42
45
  state = :text
43
46
  when :text
44
47
  if line.empty?
@@ -68,7 +71,7 @@ module Captive
68
71
  def to_s
69
72
  string = VTT_HEADER.dup
70
73
  string << "\n\n"
71
- @cue_list.each do |cue|
74
+ cues.each do |cue|
72
75
  string << milliseconds_to_timecode(cue.start_time)
73
76
  string << ' --> '
74
77
  string << milliseconds_to_timecode(cue.end_time)
@@ -81,7 +84,8 @@ module Captive
81
84
 
82
85
  # VTT Header tag matcher
83
86
  def self.validate_header(line)
84
- !!line.strip.match(/^#{VTT_HEADER}/)
87
+ # Make sure BOM does not interfere with header detection
88
+ !!line.force_encoding('UTF-8').delete("\xEF\xBB\xBF").strip.match(/^#{VTT_HEADER}/)
85
89
  end
86
90
 
87
91
  # VTT Metadata tag matcher
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Captive
4
- VERSION = '1.0.0'
4
+ VERSION = '1.1.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: captive
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - mserran2
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-13 00:00:00.000000000 Z
11
+ date: 2020-07-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -109,7 +109,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
109
109
  requirements:
110
110
  - - ">="
111
111
  - !ruby/object:Gem::Version
112
- version: '0'
112
+ version: 1.9.2
113
113
  required_rubygems_version: !ruby/object:Gem::Requirement
114
114
  requirements:
115
115
  - - ">="