voot 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4f0b1b7d1b82d3b452c84daabe479a407bd86e14
4
- data.tar.gz: c89d119e5d1365d2943e597311c567b44f063f82
3
+ metadata.gz: 4663014318069925f156ecc91d0375178add107a
4
+ data.tar.gz: d462fa6bf6fed6bcbd8b292a46b981c2255bbdb2
5
5
  SHA512:
6
- metadata.gz: e784362950fbe7b40259284438202fffbb250297340e71ebd07ec978dc78f13ee5b22f74c6d4321dc3b398b6f764da59e8fe5b770f203231d70e4f930a5b8f71
7
- data.tar.gz: 4e19b0731cb1d9507d6d3e322fdd07c7737ea6a099f71341a5e2e024563f6b0e9854ffa42f8ab60d8ab4e573e994a2f541c3ff853de856497fbce77bf2ef426a
6
+ metadata.gz: 473e633f0c06fd79742ae767d30170f0a4e6bda3ca9ba98c961ecd8b845f6634550e4f4164a42c553a5709658a3fc9ca663d91808056d2a39df0eff1bd02bfb1
7
+ data.tar.gz: d8e8f6032846b150ba72b99e2300acadc1ec3f24c4be8c0245726d0668165acab2251f1102c4d9f2412302abb15a9f2fca582241fa0604f53ca483283cc1a88c
data/Guardfile CHANGED
@@ -5,7 +5,7 @@ guard 'bundler' do
5
5
  watch(/^.+\.gemspec/)
6
6
  end
7
7
 
8
- guard :rspec do
8
+ guard :rspec, cli: "--fail-fast" do
9
9
  watch(%r{^spec/.+_spec\.rb$})
10
10
  watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
11
11
  watch('spec/spec_helper.rb') { "spec" }
data/README.md CHANGED
@@ -4,6 +4,16 @@
4
4
 
5
5
  Voot lets you read and write WebVTT files with great ease.
6
6
 
7
+ ## Usage
8
+
9
+ > vtt = Voot.load("~/Movies/My Indian Films/manty-poothon.vtt")
10
+ => #<Voot::Vtt>
11
+ > vtt.cues.length
12
+ => 1
13
+ > vtt.cues.first.payload
14
+ => "You are those knights and whatever, let me marry Srivani or we will dance"
15
+
16
+
7
17
  ## Installation
8
18
 
9
19
  Add this line to your application's Gemfile:
@@ -1,5 +1,17 @@
1
1
  require "voot/version"
2
2
  require "voot/vtt"
3
+ require "voot/parser"
4
+ require "voot/transform"
3
5
 
4
6
  module Voot
7
+ def self.load(path)
8
+ transform = Voot::Transform.new
9
+ parser = Voot::Parser.new
10
+ vtt_contents = File.read(path)
11
+ parse_tree = parser.parse(vtt_contents)
12
+
13
+ transform.apply(parse_tree).tap do |vtt|
14
+ vtt.path = path
15
+ end
16
+ end
5
17
  end
@@ -15,11 +15,9 @@ module Voot
15
15
  end
16
16
 
17
17
  def to_webvtt
18
- if has_identifier?
19
- "#{identifier}\n#{cue_timing.to_webvtt}\n#{payload}"
20
- else
21
- "#{cue_timing.to_webvtt}\n#{payload}"
22
- end
18
+ output = ""
19
+ output << identifier << "\n" if has_identifier?
20
+ output << cue_timing.to_webvtt << "\n" << payload
23
21
  end
24
22
  end
25
23
  end
@@ -4,7 +4,13 @@ module Voot
4
4
  class CueList < Array
5
5
 
6
6
  def to_webvtt
7
- map(&:to_webvtt).join("\n\n")
7
+ sorted_cues.map(&:to_webvtt).join("\n\n")
8
+ end
9
+
10
+ private
11
+
12
+ def sorted_cues
13
+ sort_by { |cue| cue.cue_timing.start_timestamp }
8
14
  end
9
15
  end
10
16
  end
@@ -1,32 +1,20 @@
1
+ require "voot/timestamp"
2
+
1
3
  module Voot
2
4
  class CueTiming
3
- attr_reader :start_seconds, :end_seconds
5
+ attr_reader :start_timestamp, :stop_timestamp
4
6
 
5
- def initialize(options = {})
6
- @start_seconds = options.fetch(:start_seconds)
7
- @end_seconds = options.fetch(:end_seconds)
7
+ def initialize(start_timestamp, stop_timestamp)
8
+ @start_timestamp = start_timestamp
9
+ @stop_timestamp = stop_timestamp
8
10
  end
9
11
 
10
12
  def to_webvtt
11
- "#{format_timestamp(start_seconds)} --> #{format_timestamp(end_seconds)}"
12
- end
13
-
14
- private
15
-
16
- def format_timestamp(seconds)
17
- "#{format_minutes(seconds)}:#{format_seconds(seconds)}.#{format_subseconds(seconds)}"
18
- end
19
-
20
- def format_minutes(seconds)
21
- (seconds.to_i / 60).to_s.rjust(2, "0")
22
- end
23
-
24
- def format_seconds(seconds)
25
- (seconds.to_i % 60).to_s.rjust(2, "0")
13
+ start_timestamp.to_webvtt << " --> " << stop_timestamp.to_webvtt
26
14
  end
27
15
 
28
- def format_subseconds(seconds)
29
- ((seconds * 1000).round % 1000).to_s.rjust(3, "0")
16
+ def cover?(seconds)
17
+ seconds.between?(start_timestamp.seconds_since_origin, stop_timestamp.seconds_since_origin)
30
18
  end
31
19
  end
32
20
  end
@@ -0,0 +1,43 @@
1
+ require "parslet"
2
+
3
+ module Voot
4
+ class Parser < Parslet::Parser
5
+ rule(:line_break) { match("[\r\n]|\r\n") }
6
+ rule(:whitespace) { match("[\s\t]") }
7
+ rule(:section_separator) { line_break.repeat(2) }
8
+
9
+ rule(:header_string) { (line_break.absent? >> any).repeat }
10
+ rule(:header) { str("WEBVTT") >> whitespace.repeat >> header_string.as(:header) }
11
+
12
+ rule(:digit) { match("[0-9]") }
13
+ rule(:colon) { match("[:]") }
14
+ rule(:period) { match("[.]") }
15
+
16
+ rule(:hours) { digit.repeat(2).as(:hours) >> colon }
17
+ rule(:minutes) { digit.repeat(2, 2).as(:minutes) >> colon }
18
+ rule(:seconds) { digit.repeat(2, 2).as(:seconds) >> period }
19
+ rule(:subseconds) { digit.repeat(3, 3).as(:subseconds) }
20
+
21
+ rule(:timestamp_without_hours) { minutes >> seconds >> subseconds }
22
+ rule(:timestamp_with_hours) { hours >> minutes >> seconds >> subseconds }
23
+
24
+ rule(:timestamp) { timestamp_without_hours | timestamp_with_hours }
25
+
26
+ rule(:timing_delimiter) { str("-->") }
27
+ rule(:cue_timing) { timestamp.as(:start) >> whitespace.repeat(1) >> timing_delimiter >> whitespace.repeat(1) >> timestamp.as(:stop) }
28
+
29
+ rule(:cue_identifier) { (line_break.absent? >> timing_delimiter.absent? >> any).repeat.as(:identifier) }
30
+
31
+ rule(:cue_payload) { (section_separator.absent? >> any).repeat.as(:payload) }
32
+
33
+ rule(:cue_without_identifier) { cue_timing >> line_break >> cue_payload }
34
+ rule(:cue_with_identifier) { cue_identifier >> line_break >> cue_timing >> line_break >> cue_payload }
35
+
36
+ rule(:cue) { cue_without_identifier | cue_with_identifier }
37
+ rule(:cue_list) { (section_separator >> cue).repeat }
38
+
39
+ rule(:file_body) { header >> cue_list.as(:cues) >> line_break.repeat }
40
+
41
+ root(:file_body)
42
+ end
43
+ end
@@ -0,0 +1,49 @@
1
+ module Voot
2
+ class Timestamp
3
+ include Comparable
4
+
5
+ attr_reader :seconds_since_origin
6
+
7
+ def initialize(seconds_since_origin)
8
+ @seconds_since_origin = seconds_since_origin.to_f.round(3)
9
+ end
10
+
11
+ def <=>(other)
12
+ if other.is_a?(Numeric)
13
+ seconds_since_origin <=> other
14
+ else
15
+ seconds_since_origin <=> other.seconds_since_origin
16
+ end
17
+ end
18
+
19
+ def hours
20
+ seconds_since_origin.to_i / 3600
21
+ end
22
+
23
+ def minutes
24
+ seconds_without_hours.to_i / 60
25
+ end
26
+
27
+ def seconds
28
+ seconds_since_origin.to_i % 60
29
+ end
30
+
31
+ def milliseconds
32
+ milliseconds_since_origin.round % 1000
33
+ end
34
+
35
+ def to_webvtt
36
+ "%02d:%02d:%02d.%03d" % [hours, minutes, seconds, milliseconds]
37
+ end
38
+
39
+ private
40
+
41
+ def milliseconds_since_origin
42
+ seconds_since_origin * 1000
43
+ end
44
+
45
+ def seconds_without_hours
46
+ seconds_since_origin % 3600
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,35 @@
1
+ require "parslet"
2
+
3
+ module Voot
4
+ class Transform < Parslet::Transform
5
+ rule(:minutes => simple(:minutes), :seconds => simple(:seconds), :subseconds => simple(:subseconds)) do
6
+ Voot::Timestamp.new((minutes.to_i * 60) + seconds.to_i + (subseconds.to_i / 1000.0))
7
+ end
8
+
9
+ rule(:hours => simple(:hours), :minutes => simple(:minutes), :seconds => simple(:seconds), :subseconds => simple(:subseconds)) do
10
+ Voot::Timestamp.new((hours.to_i * 3600) + (minutes.to_i * 60) + seconds.to_i + (subseconds.to_i / 1000.0))
11
+ end
12
+
13
+ rule(:start => simple(:start_timestamp), :stop => simple(:stop_timestamp), :payload => simple(:payload)) do
14
+ cue_timing = Voot::CueTiming.new(start_timestamp, stop_timestamp)
15
+ Voot::Cue.new(cue_timing: cue_timing, payload: payload.to_s)
16
+ end
17
+
18
+ rule(:start => simple(:start_timestamp), :stop => simple(:stop_timestamp), :payload => simple(:payload), :identifier => simple(:identifier)) do
19
+ cue_timing = Voot::CueTiming.new(start_timestamp, stop_timestamp)
20
+ Voot::Cue.new(cue_timing: cue_timing, payload: payload.to_s, identifier: identifier.to_s)
21
+ end
22
+
23
+ rule(:header => sequence(:header), :cues => sequence(:cues)) do
24
+ vtt = Voot::Vtt.new
25
+ cues.each { |cue| vtt.cues << cue }
26
+ vtt
27
+ end
28
+
29
+ rule(:header => simple(:header), :cues => sequence(:cues)) do
30
+ vtt = Voot::Vtt.new(header: header)
31
+ cues.each { |cue| vtt.cues << cue }
32
+ vtt
33
+ end
34
+ end
35
+ end
@@ -1,3 +1,3 @@
1
1
  module Voot
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -2,11 +2,10 @@ require "voot/cue_list"
2
2
 
3
3
  module Voot
4
4
  class Vtt
5
- attr_reader :path
6
- attr_accessor :header
5
+ attr_accessor :header, :path
7
6
 
8
7
  def initialize(options = {})
9
- @path = options.fetch(:path)
8
+ @path = options[:path]
10
9
  @header = options[:header]
11
10
  end
12
11
 
@@ -11,12 +11,36 @@ describe Voot::CueList do
11
11
  context "when the list has an item in it" do
12
12
  before { cue_list << create_cue("reasonably priced", 0, 0) }
13
13
 
14
- its(:to_webvtt) { should == "00:00.000 --> 00:00.000\nreasonably priced" }
14
+ its(:to_webvtt) { should == "00:00:00.000 --> 00:00:00.000\nreasonably priced" }
15
15
 
16
16
  context "when the list has multiple items" do
17
17
  before { cue_list << create_cue("toiletries", 1, 1) }
18
18
 
19
- its(:to_webvtt) { should include "00:01.000 --> 00:01.000\ntoiletries" }
19
+ its(:to_webvtt) { should include "00:00:01.000 --> 00:00:01.000\ntoiletries" }
20
+
21
+ it "separates cues with a double newline" do
22
+ cue_list.to_webvtt.should == <<-ALL_THE_CUES.strip
23
+ 00:00:00.000 --> 00:00:00.000
24
+ reasonably priced
25
+
26
+ 00:00:01.000 --> 00:00:01.000
27
+ toiletries
28
+ ALL_THE_CUES
29
+ end
30
+
31
+ it "orders the cues by start time" do
32
+ cue_list << create_cue("hair spray", 0.5, 1)
33
+ cue_list.to_webvtt.should == <<-ALL_THE_CUES.strip
34
+ 00:00:00.000 --> 00:00:00.000
35
+ reasonably priced
36
+
37
+ 00:00:00.500 --> 00:00:01.000
38
+ hair spray
39
+
40
+ 00:00:01.000 --> 00:00:01.000
41
+ toiletries
42
+ ALL_THE_CUES
43
+ end
20
44
  end
21
45
  end
22
46
  end
@@ -3,7 +3,7 @@ require "spec_helper"
3
3
  describe Voot::Cue do
4
4
  let(:identifier) { "taco massacre" }
5
5
  let(:payload) { "tasty" }
6
- let(:cue_timing) { Voot::CueTiming.new(start_seconds: 0, end_seconds: 1) }
6
+ let(:cue_timing) { create_timing(0, 1) }
7
7
 
8
8
  subject(:cue) do
9
9
  Voot::Cue.new(
@@ -33,13 +33,13 @@ describe Voot::Cue do
33
33
 
34
34
  describe "#to_webvtt" do
35
35
  context "when the identifier is set" do
36
- its(:to_webvtt) { should == "taco massacre\n00:00.000 --> 00:01.000\ntasty" }
36
+ its(:to_webvtt) { should == "taco massacre\n00:00:00.000 --> 00:00:01.000\ntasty" }
37
37
  end
38
38
 
39
39
  context "when the identifier is not set" do
40
40
  let(:identifier) { nil }
41
41
 
42
- its(:to_webvtt) { should == "00:00.000 --> 00:01.000\ntasty" }
42
+ its(:to_webvtt) { should == "00:00:00.000 --> 00:00:01.000\ntasty" }
43
43
  end
44
44
  end
45
45
  end
@@ -1,15 +1,15 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe Voot::CueTiming do
4
- let(:start_seconds) { 1.0001 }
5
- let(:end_seconds) { 65.0009 }
6
-
7
- subject(:cue_timing) do
8
- Voot::CueTiming.new(
9
- start_seconds: start_seconds,
10
- end_seconds: end_seconds
11
- )
12
- end
4
+ let(:start_timestamp) { Voot::Timestamp.new(1.0001) }
5
+ let(:stop_timestamp) { Voot::Timestamp.new(65.0009) }
6
+
7
+ subject(:cue_timing) { Voot::CueTiming.new(start_timestamp, stop_timestamp) }
13
8
 
14
- its(:to_webvtt) { should == "00:01.000 --> 01:05.001" }
9
+ its(:to_webvtt) { should == "00:00:01.000 --> 00:01:05.001" }
10
+
11
+ describe "#cover?" do
12
+ it { should cover 1.000 }
13
+ it { should cover 65.001 }
14
+ end
15
15
  end
@@ -0,0 +1,82 @@
1
+ require "spec_helper"
2
+
3
+ describe Voot::Parser do
4
+ subject(:parser) { Voot::Parser.new }
5
+
6
+ it { should parse("WEBVTT") }
7
+ it { should parse("WEBVTT\n\n") }
8
+
9
+ describe "#header" do
10
+ specify { parser.header.should parse("WEBVTT").as(header: []) }
11
+ specify { parser.header.should parse("WEBVTT - goose hugs").as(header: "- goose hugs") }
12
+ end
13
+
14
+ describe "#subseconds" do
15
+ specify { parser.subseconds.should parse("164").as(subseconds: "164") }
16
+ end
17
+
18
+ describe "#seconds" do
19
+ specify { parser.seconds.should parse("66.").as(seconds: "66") }
20
+ end
21
+
22
+ describe "#minutes" do
23
+ specify { parser.minutes.should parse("01:").as(minutes: "01") }
24
+ end
25
+
26
+ describe "#hours" do
27
+ specify { parser.hours.should parse("13:").as(hours: "13") }
28
+ specify { parser.hours.should parse("9000:").as(hours: "9000") }
29
+ end
30
+
31
+ describe "#timestamp_without_hours" do
32
+ specify { parser.timestamp_without_hours.should parse("01:33.007").as(minutes: "01", seconds: "33", subseconds: "007") }
33
+ end
34
+
35
+ describe "#timestamp_with_hours" do
36
+ specify { parser.timestamp_with_hours.should parse("11:59:59.999").as(hours: "11", minutes: "59", seconds: "59", subseconds: "999") }
37
+ end
38
+
39
+ describe "#timestamp" do
40
+ specify { parser.timestamp.should parse("01:33.007").as(minutes: "01", seconds: "33", subseconds: "007") }
41
+ specify { parser.timestamp.should parse("11:59:59.999").as(hours: "11", minutes: "59", seconds: "59", subseconds: "999") }
42
+ end
43
+
44
+ describe "#timing_delimiter" do
45
+ specify { parser.timing_delimiter.should parse("-->") }
46
+ end
47
+
48
+ describe "#cue_timing" do
49
+ specify { parser.cue_timing.should parse("01:33.007 -->\t 11:59:59.999").as(start: anything, stop: anything) }
50
+ end
51
+
52
+ describe "#cue_identifier" do
53
+ specify { parser.cue_identifier.should_not parse("drugboats --> ahoy") }
54
+ specify { parser.cue_identifier.should_not parse("puppet\npals") }
55
+ specify { parser.cue_identifier.should parse("evil squid cult").as(identifier: "evil squid cult") }
56
+ end
57
+
58
+ describe "#cue_payload" do
59
+ specify { parser.cue_payload.should parse("once upon a dingus").as(payload: "once upon a dingus") }
60
+ specify { parser.cue_payload.should parse("there were\nan prancess").as(payload: "there were\nan prancess") }
61
+ specify { parser.cue_payload.should_not parse("whare is\n\nlazy prance") }
62
+ end
63
+
64
+ describe "#cue_without_identifier" do
65
+ specify { parser.cue_without_identifier.should parse("00:00.000 --> 00:30.000\nget to the choppa?").as(start: anything, stop: anything, payload: anything) }
66
+ end
67
+
68
+ describe "#cue_with_identifier" do
69
+ specify { parser.cue_with_identifier.should parse("777\n00:00.000 --> 00:30.000\njhonny paints the wall").as(identifier: anything, start: anything, stop: anything, payload: anything) }
70
+ end
71
+
72
+ describe "#cue" do
73
+ specify { parser.cue.should parse("the hablet\n00:00.000 --> 00:30.000\nwelcam ashire\nbalbo").as(identifier: anything, start: anything, stop: anything, payload: anything) }
74
+ specify { parser.cue.should parse("04:00.000 --> 55:30.000\n(tom belmadal sings)").as(start: anything, stop: anything, payload: anything) }
75
+ end
76
+
77
+ describe "#file_body" do
78
+ specify { parser.file_body.should parse("WEBVTT").as(header: [], cues: []) }
79
+ specify { parser.file_body.should parse("WEBVTT\n\n00:00.000 --> 00:00.000\nhi").as(header: [], cues: [anything]) }
80
+ specify { parser.file_body.should parse("WEBVTT\n\n00:00.000 --> 00:00.000\nlike\n\n00:01.000 --> 00:01.000\nwut").as(header: [], cues: [anything, anything]) }
81
+ end
82
+ end
@@ -0,0 +1,91 @@
1
+ require "spec_helper"
2
+
3
+ describe Voot::Timestamp do
4
+ subject(:timestamp) { Voot::Timestamp.new(seconds) }
5
+
6
+ describe "#<=>" do
7
+ let(:seconds) { 1.1 }
8
+
9
+ context "when comparing against the number of seconds" do
10
+ it { should == seconds }
11
+ end
12
+
13
+ context "when comparing against another timestamp" do
14
+ let(:other) { double(:timestamp, seconds_since_origin: seconds) }
15
+
16
+ it { should == other }
17
+ end
18
+ end
19
+
20
+ describe "#seconds_since_origin" do
21
+ context "when the seconds can round up" do
22
+ let(:seconds) { 0.0005 }
23
+
24
+ it "rounds the seconds to the nearest millisecond" do
25
+ timestamp.seconds_since_origin.should == 0.001
26
+ end
27
+ end
28
+
29
+ context "when the seconds can round up" do
30
+ let(:seconds) { 0.0014 }
31
+
32
+ it "rounds the seconds to the nearest millisecond" do
33
+ timestamp.seconds_since_origin.should == 0.001
34
+ end
35
+ end
36
+ end
37
+
38
+ describe "#hours" do
39
+ let(:seconds) { 3600 }
40
+
41
+ it "returns the number of hours in the timestamp" do
42
+ timestamp.hours.should == 1
43
+ end
44
+ end
45
+
46
+ describe "#minutes" do
47
+ let(:seconds) { 120 }
48
+
49
+ it "returns the number of minutes in the timestamp" do
50
+ timestamp.minutes.should == 2
51
+ end
52
+ end
53
+
54
+ describe "#seconds" do
55
+ let(:seconds) { 3 }
56
+
57
+ it "returns the number of seconds in the timestamp" do
58
+ timestamp.seconds.should == 3
59
+ end
60
+ end
61
+
62
+ describe "#milliseconds" do
63
+ let(:seconds) { 0.004 }
64
+
65
+ it "returns the number of milliseconds in the timestamp" do
66
+ timestamp.milliseconds.should == 4
67
+ end
68
+
69
+ context "when milliseconds can round up" do
70
+ let(:seconds) { 0.0045 }
71
+
72
+ it "returns the rounded-up number of milliseconds in the timestamp" do
73
+ timestamp.milliseconds.should == 5
74
+ end
75
+ end
76
+
77
+ context "when milliseconds can round down" do
78
+ let(:seconds) { 0.0031 }
79
+
80
+ it "returns the rounded-down number of milliseconds in the timestamp" do
81
+ timestamp.milliseconds.should == 3
82
+ end
83
+ end
84
+ end
85
+
86
+ describe "#to_webvtt" do
87
+ let(:seconds) { 3723.004 }
88
+
89
+ its(:to_webvtt) { should == "01:02:03.004" }
90
+ end
91
+ end
@@ -0,0 +1,72 @@
1
+ require "spec_helper"
2
+
3
+ describe Voot::Transform do
4
+ let(:cue_timing) { create_timing(61.001, 3661.001) }
5
+ let(:parser) { Voot::Parser.new }
6
+
7
+ subject(:transformed) { Voot::Transform.new.apply(parsed_input) }
8
+
9
+ describe "#timestamp" do
10
+ let(:parsed_input) { parser.timestamp.parse(input_string) }
11
+
12
+ context "when the timestamp does not have hours" do
13
+ let(:input_string) { "01:01.001" }
14
+
15
+ it { should == 61.001 }
16
+ end
17
+
18
+ context "when the timestamp has hours" do
19
+ let(:input_string) { "01:01:01.001" }
20
+
21
+ it { should == 3661.001 }
22
+ end
23
+ end
24
+
25
+ describe "#cue_timing" do
26
+ let(:input_string) { cue_timing.to_webvtt }
27
+ let(:parsed_input) { parser.cue_timing.parse(input_string) }
28
+
29
+ it { should == {start: 61.001, stop: 3661.001} }
30
+ end
31
+
32
+ describe "#cue" do
33
+ let(:parsed_input) { parser.cue.parse(cue.to_webvtt) }
34
+
35
+ context "when there is an identifier" do
36
+ let(:cue) { Voot::Cue.new(cue_timing: cue_timing, identifier: "ferdo", payload: "hes also too an habblet") }
37
+
38
+ its(:identifier) { should == "ferdo" }
39
+ its(:payload) { should == "hes also too an habblet" }
40
+ its(:cue_timing) { should cover 61.001 }
41
+ its(:cue_timing) { should cover 3661.001 }
42
+ end
43
+
44
+ context "when there is no identifier" do
45
+ let(:cue) { Voot::Cue.new(cue_timing: cue_timing, payload: "gordolf he is a beard") }
46
+
47
+ its(:identifier) { should be_nil }
48
+ its(:payload) { should == "gordolf he is a beard" }
49
+ its(:cue_timing) { should cover 61.001 }
50
+ its(:cue_timing) { should cover 3661.001 }
51
+ end
52
+ end
53
+
54
+ describe "#file_body" do
55
+ let(:vtt) { Voot::Vtt.new.tap { |vtt| vtt.cues << cue } }
56
+ let(:cue) { Voot::Cue.new(cue_timing: cue_timing, payload: "borgomar is a dumb") }
57
+ let(:parsed_input) { parser.file_body.parse(vtt.to_webvtt) }
58
+
59
+ context "when the header is not set" do
60
+
61
+ its(:header) { should be_nil }
62
+ its(:cues) { should have(1).cue }
63
+ end
64
+
65
+ context "when the header is set" do
66
+ before { vtt.header = "lerd of rang" }
67
+
68
+ its(:header) { should == "lerd of rang" }
69
+ its(:cues) { should have(1).cue }
70
+ end
71
+ end
72
+ end
@@ -19,7 +19,7 @@ describe Voot::Vtt do
19
19
  context "when the file exists" do
20
20
  let(:pesky_user_data) { "hey guys tacos are great right" }
21
21
 
22
- before { File.write(vtt_path, pesky_user_data) }
22
+ before { File.open(vtt_path, "w") { |file| file << pesky_user_data } }
23
23
 
24
24
  it "clobbers the file content" do
25
25
  expect do
@@ -65,7 +65,7 @@ describe Voot::Vtt do
65
65
  context "when there is a cue" do
66
66
  before { vtt.cues << create_cue("not the drill!", 0, 2) }
67
67
 
68
- its(:to_webvtt) { should == "WEBVTT\n\n00:00.000 --> 00:02.000\nnot the drill!" }
68
+ its(:to_webvtt) { should == "WEBVTT\n\n00:00:00.000 --> 00:00:02.000\nnot the drill!" }
69
69
  end
70
70
  end
71
71
  end
@@ -0,0 +1,51 @@
1
+ require "spec_helper"
2
+
3
+ describe Voot do
4
+ let(:voot_path) { Tempfile.new("voot").path }
5
+
6
+ describe ".load" do
7
+ subject { Voot.load(voot_path) }
8
+
9
+ context "when the file is empty" do
10
+ it "fails to parse" do
11
+ expect { Voot.load(voot_path) }.to raise_error(Parslet::ParseFailed)
12
+ end
13
+ end
14
+
15
+ context "when the file does not contain a webvtt header" do
16
+ before { File.open(voot_path, "w") { |file| file << "taco thursdays yo" } }
17
+
18
+ it "fails to parse" do
19
+ expect { Voot.load(voot_path) }.to raise_error(Parslet::ParseFailed)
20
+ end
21
+ end
22
+
23
+ context "when the file contains a webvtt header" do
24
+ before { File.open(voot_path, "w") { |file| file << "WEBVTT" } }
25
+
26
+ its(:cues) { should be_empty }
27
+ its(:header) { should be_nil }
28
+ its(:path) { should == voot_path }
29
+ end
30
+
31
+ context "when the file contains a header" do
32
+ before { File.open(voot_path, "w") { |file| file << "WEBVTT presents bob ross classics" } }
33
+
34
+ its(:header) { should == "presents bob ross classics" }
35
+ end
36
+
37
+ context "when the file contains a junk-filled header" do
38
+ before { File.open(voot_path, "w") { |file| file << "WEBVTT\ni liek ppokemans" } }
39
+
40
+ it "fails to parse" do
41
+ expect { Voot.load(voot_path) }.to raise_error(Parslet::ParseFailed)
42
+ end
43
+ end
44
+
45
+ context "when the file contains cues" do
46
+ before { File.open(voot_path, "w") { |file| file << "WEBVTT\n\n00:00.000 --> 00:00.000\ndrugboat" } }
47
+
48
+ it { should have(1).cues }
49
+ end
50
+ end
51
+ end
@@ -1,6 +1,8 @@
1
1
  $:<< File.expand_path("../../lib", __FILE__)
2
2
 
3
3
  require "voot"
4
+ require "tempfile"
5
+ require "parslet/rig/rspec"
4
6
 
5
7
  Dir.glob(File.expand_path("../support/**/*.rb", __FILE__)) { |f| require f }
6
8
 
@@ -1,6 +1,12 @@
1
1
  module CueHelper
2
+ def create_timing(start, stop)
3
+ start_timestamp = Voot::Timestamp.new(start)
4
+ stop_timestamp = Voot::Timestamp.new(stop)
5
+ Voot::CueTiming.new(start_timestamp, stop_timestamp)
6
+ end
7
+
2
8
  def create_cue(payload, start, stop)
3
- cue_timing = Voot::CueTiming.new(start_seconds: start, end_seconds: stop)
9
+ cue_timing = create_timing(start, stop)
4
10
  Voot::Cue.new(cue_timing: cue_timing, payload: payload)
5
11
  end
6
12
  end
@@ -18,6 +18,8 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = `git ls-files -- spec`.split("\n")
19
19
  spec.require_paths = ["lib"]
20
20
 
21
+ spec.add_dependency "parslet"
22
+
21
23
  spec.add_development_dependency "rspec"
22
24
  spec.add_development_dependency "guard-rspec"
23
25
  spec.add_development_dependency "guard-bundler"
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: voot
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Doc Ritezel
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-06-30 00:00:00.000000000 Z
11
+ date: 2013-07-01 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: parslet
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: rspec
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -87,13 +101,20 @@ files:
87
101
  - lib/voot/cue.rb
88
102
  - lib/voot/cue_list.rb
89
103
  - lib/voot/cue_timing.rb
104
+ - lib/voot/parser.rb
105
+ - lib/voot/timestamp.rb
106
+ - lib/voot/transform.rb
90
107
  - lib/voot/version.rb
91
108
  - lib/voot/vtt.rb
92
109
  - script/ci.sh
93
110
  - spec/lib/voot/cue_list_spec.rb
94
111
  - spec/lib/voot/cue_spec.rb
95
112
  - spec/lib/voot/cue_timing_spec.rb
113
+ - spec/lib/voot/parser_spec.rb
114
+ - spec/lib/voot/timestamp_spec.rb
115
+ - spec/lib/voot/transform_spec.rb
96
116
  - spec/lib/voot/vtt_spec.rb
117
+ - spec/lib/voot_spec.rb
97
118
  - spec/spec_helper.rb
98
119
  - spec/support/cue_helper.rb
99
120
  - voot.gemspec
@@ -125,6 +146,10 @@ test_files:
125
146
  - spec/lib/voot/cue_list_spec.rb
126
147
  - spec/lib/voot/cue_spec.rb
127
148
  - spec/lib/voot/cue_timing_spec.rb
149
+ - spec/lib/voot/parser_spec.rb
150
+ - spec/lib/voot/timestamp_spec.rb
151
+ - spec/lib/voot/transform_spec.rb
128
152
  - spec/lib/voot/vtt_spec.rb
153
+ - spec/lib/voot_spec.rb
129
154
  - spec/spec_helper.rb
130
155
  - spec/support/cue_helper.rb