timecode 0.1.7 → 0.1.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/History.txt +7 -1
  2. data/lib/timecode.rb +26 -18
  3. data/test/test_timecode.rb +43 -12
  4. metadata +2 -2
data/History.txt CHANGED
@@ -1,4 +1,10 @@
1
- === 0.1.6 / 2009-01-31
1
+ === 0.1.8 / 2009-01-31
2
+
3
+ * Bail out with empty or whitespace strings passed to Timecode.parse
4
+ * Add Timecode#to_seconds that returns a float value (useful for Quicktime)
5
+ * Move the validation routine into the class itself
6
+
7
+ === 0.1.7 / 2009-01-31
2
8
 
3
9
  * Simplified parsing code, more safeguards and needless exceptions removed
4
10
 
data/lib/timecode.rb CHANGED
@@ -12,7 +12,7 @@
12
12
  # :mapping => [%w(source_tc_frames total), %w(tape_fps fps)]
13
13
 
14
14
  class Timecode
15
- VERSION = '0.1.7'
15
+ VERSION = '0.1.8'
16
16
 
17
17
  include Comparable
18
18
 
@@ -78,13 +78,13 @@ class Timecode
78
78
  # * 00:00:00:00 - will be parsed as zero TC
79
79
  def parse(input, with_fps = DEFAULT_FPS)
80
80
  # Drop frame goodbye
81
- raise Error, "We do not support drop frame" if (input =~ DF_TC_RE)
82
-
83
- hrs, mins, secs, frames = 0,0,0,0
84
- atoms = []
85
-
81
+ if (input =~ DF_TC_RE)
82
+ raise Error, "We do not support drop-frame TC"
83
+ # No empty values
84
+ elsif (input =~ /A(\s+)Z/)
85
+ raise CannotParse, "Empty timecode"
86
86
  # 00:00:00:00
87
- if (input =~ COMPLETE_TC_RE)
87
+ elsif (input =~ COMPLETE_TC_RE)
88
88
  atoms_and_fps = input.scan(COMPLETE_TC_RE).to_a.flatten.map{|e| Integer(e)} + [with_fps]
89
89
  return at(*atoms_and_fps)
90
90
  # 00:00:00.0
@@ -92,7 +92,9 @@ class Timecode
92
92
  parse_with_fractional_seconds(input, with_fps)
93
93
  # 10h 20m 10s 1f 00:00:00:01 - space separated is a sum of parts
94
94
  elsif input =~ /\s/
95
- return input.split.map{|part| parse(part, with_fps) }.inject { |sum, p| sum + p.total }
95
+ parts = input.gsub(/\s/, ' ').split.reject{|e| e.strip.empty? }
96
+ raise CannotParse, "No atoms" if parts.empty?
97
+ parts.map{|part| parse(part, with_fps) }.inject{|sum, p| sum + p.total }
96
98
  # 10s
97
99
  elsif input =~ /^(\d+)s$/
98
100
  return new(input.to_i * with_fps, with_fps)
@@ -119,6 +121,13 @@ class Timecode
119
121
 
120
122
  # Initialize a Timecode object at this specfic timecode
121
123
  def at(hrs, mins, secs, frames, with_fps = DEFAULT_FPS)
124
+ validate_atoms!(hrs, mins, secs, frames, with_fps)
125
+ total = (hrs*(60*60*with_fps) + mins*(60*with_fps) + secs*with_fps + frames).round
126
+ new(total, with_fps)
127
+ end
128
+
129
+ # Validate the passed atoms for the concrete framerate
130
+ def validate_atoms!(hrs, mins, secs, frames, with_fps)
122
131
  case true
123
132
  when hrs > 99
124
133
  raise RangeError, "There can be no more than 99 hours, got #{hrs}"
@@ -129,9 +138,6 @@ class Timecode
129
138
  when frames > (with_fps -1)
130
139
  raise RangeError, "There can be no more than #{with_fps -1} frames @#{with_fps}, got #{frames}"
131
140
  end
132
-
133
- total = (hrs*(60*60*with_fps) + mins*(60*with_fps) + secs*with_fps + frames).round
134
- new(total, with_fps)
135
141
  end
136
142
 
137
143
  # Parse a timecode with fractional seconds instead of frames. This is how ffmpeg reports
@@ -217,6 +223,11 @@ class Timecode
217
223
  uint
218
224
  end
219
225
 
226
+ # get the timecode as a floating-point number of seconds (used in Quicktime)
227
+ def to_seconds
228
+ (@total / @fps)
229
+ end
230
+
220
231
  # Convert to different framerate based on the total frames. Therefore,
221
232
  # 1 second of PAL video will convert to 25 frames of NTSC (this
222
233
  # is suitable for PAL to film TC conversions and back).
@@ -305,7 +316,7 @@ class Timecode
305
316
 
306
317
  private
307
318
 
308
- # Formats the actual timecode output from the number of frames
319
+ # Prepare and format the values for TC output
309
320
  def validate!
310
321
  frames = @total
311
322
  secs = (@total.to_f/@fps).floor
@@ -314,12 +325,9 @@ class Timecode
314
325
  secs -= (mins*60)
315
326
  hrs = (mins/60).floor
316
327
  mins-= (hrs*60)
317
-
318
- raise RangeError, "Timecode cannot be longer that 99 hrs" if hrs > 99
319
- raise RangeError, "More than 59 minutes" if mins > 59
320
- raise RangeError, "More than 59 seconds" if secs > 59
321
- raise RangeError, "More than #{@fps.to_s} frames (#{frames}) in the last second" if frames >= @fps
322
-
328
+
329
+ self.class.validate_atoms!(hrs, mins, secs, frames, @fps)
330
+
323
331
  [hrs, mins, secs, frames]
324
332
  end
325
333
 
@@ -5,7 +5,7 @@ require 'test/spec'
5
5
  require File.dirname(__FILE__) + '/../lib/timecode'
6
6
 
7
7
 
8
- context "Timecode.new() should" do
8
+ context "Timecode.new should" do
9
9
 
10
10
  specify "instantiate from int" do
11
11
  tc = Timecode.new(10)
@@ -25,9 +25,35 @@ context "Timecode.new() should" do
25
25
  specify "accept full string SMPTE timecode as well" do
26
26
  Timecode.new("00:25:30:10", 25).should.equal Timecode.parse("00:25:30:10")
27
27
  end
28
+
29
+ end
30
+
31
+ context "Timecode.validate_atoms! should" do
32
+
33
+ specify "disallow more than 99 hrs" do
34
+ lambda{ Timecode.validate_atoms!(99,0,0,0, 25) }.should.not.raise
35
+ lambda{ Timecode.validate_atoms!(100,0,0,0, 25) }.should.raise(Timecode::RangeError)
36
+ end
37
+
38
+ specify "disallow more than 59 minutes" do
39
+ lambda{ Timecode.validate_atoms!(1,60,0,0, 25) }.should.raise(Timecode::RangeError)
40
+ end
41
+
42
+ specify "disallow more than 59 seconds" do
43
+ lambda{ Timecode.validate_atoms!(1,0,60,0, 25) }.should.raise(Timecode::RangeError)
44
+ end
45
+
46
+ specify "disallow more frames than what the framerate permits" do
47
+ lambda{ Timecode.validate_atoms!(1,0,60,25, 25) }.should.raise(Timecode::RangeError)
48
+ lambda{ Timecode.validate_atoms!(1,0,60,32, 30) }.should.raise(Timecode::RangeError)
49
+ end
50
+
51
+ specify "pass validation with usable values" do
52
+ lambda{ Timecode.validate_atoms!(20, 20, 10, 5, 25)}.should.not.raise
53
+ end
28
54
  end
29
55
 
30
- context "Timecode.at() should" do
56
+ context "Timecode.at should" do
31
57
 
32
58
  specify "disallow more than 99 hrs" do
33
59
  lambda{ Timecode.at(99,0,0,0) }.should.not.raise
@@ -130,6 +156,18 @@ context "A Timecode of zero should" do
130
156
  end
131
157
  end
132
158
 
159
+ context "Timecode#to_seconds should" do
160
+ specify "return a float" do
161
+ Timecode.new(0).to_seconds.should.be.kind_of Float
162
+ end
163
+
164
+ specify "return the value in seconds" do
165
+ fps = 24
166
+ secs = 126.3
167
+ Timecode.new(fps * secs, fps).to_seconds.should.be.close 126.3, 0.1
168
+ end
169
+ end
170
+
133
171
  context "An existing Timecode on inspection should" do
134
172
  specify "properly present himself via inspect" do
135
173
  Timecode.new(10, 25).inspect.should.equal "#<Timecode:00:00:00:10 (10F@25.00)>"
@@ -271,16 +309,6 @@ context "Timecode.parse should" do
271
309
  simple_tc = "00:10:34:10"
272
310
  Timecode.parse(simple_tc).to_s.should.equal(simple_tc)
273
311
  end
274
-
275
- specify "handle complete SMPTE timecode via new" do
276
- simple_tc = "00:10:34:10"
277
- Timecode.new(simple_tc).to_s.should.equal(simple_tc)
278
- end
279
-
280
- specify "refuse to handle timecode that is out of range for the framerate" do
281
- bad_tc = "00:76:89:30"
282
- lambda { Timecode.parse(bad_tc, 25) }.should.raise(Timecode::RangeError)
283
- end
284
312
 
285
313
  specify "parse a row of numbers as parts of a timecode starting from the right" do
286
314
  Timecode.parse("10").should.equal Timecode.new(10)
@@ -354,6 +382,9 @@ context "Timecode.parse should" do
354
382
  lambda { Timecode.parse("", 25) }.should.raise Timecode::CannotParse
355
383
  end
356
384
 
385
+ specify "raise on empty argument" do
386
+ lambda { Timecode.parse(" \n\n ", 25) }.should.raise Timecode::CannotParse
387
+ end
357
388
  end
358
389
 
359
390
  context "Timecode.soft_parse should" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: timecode
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Julik
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-01-31 00:00:00 +01:00
12
+ date: 2009-02-04 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency