julik-timecode 0.1.7 → 0.1.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -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/SPECS.txt CHANGED
@@ -1,11 +1,18 @@
1
1
 
2
- == Timecode.new() should
2
+ == Timecode.new should
3
3
  * instantiate from int
4
4
  * always coerce FPS to float
5
5
  * create a zero TC with no arguments
6
6
  * accept full string SMPTE timecode as well
7
7
 
8
- == Timecode.at() should
8
+ == Timecode.validate_atoms! should
9
+ * disallow more than 99 hrs
10
+ * disallow more than 59 minutes
11
+ * disallow more than 59 seconds
12
+ * disallow more frames than what the framerate permits
13
+ * pass validation with usable values
14
+
15
+ == Timecode.at should
9
16
  * disallow more than 99 hrs
10
17
  * disallow more than 59 minutes
11
18
  * disallow more than 59 seconds
@@ -30,6 +37,10 @@
30
37
  == A Timecode of zero should
31
38
  * properly respond to zero?
32
39
 
40
+ == Timecode#to_seconds should
41
+ * return a float
42
+ * return the value in seconds
43
+
33
44
  == An existing Timecode on inspection should
34
45
  * properly present himself via inspect
35
46
  * properly print itself
@@ -64,8 +75,6 @@
64
75
 
65
76
  == Timecode.parse should
66
77
  * handle complete SMPTE timecode
67
- * handle complete SMPTE timecode via new
68
- * refuse to handle timecode that is out of range for the framerate
69
78
  * parse a row of numbers as parts of a timecode starting from the right
70
79
  * parse a number with f suffix as frames
71
80
  * parse a number with s suffix as seconds
@@ -78,6 +87,7 @@
78
87
  * parse timecode with fractional second instead of frames
79
88
  * raise when trying to parse DF timecode
80
89
  * raise on improper format
90
+ * raise on empty argument
81
91
 
82
92
  == Timecode.soft_parse should
83
93
  * parse the timecode
@@ -87,4 +97,4 @@
87
97
  * parse from a 4x4bits packed 32bit unsigned int
88
98
  * properly convert itself back to 4x4 bits 32bit unsigned int
89
99
 
90
- 60 specifications (101 requirements), 0 failures
100
+ 66 specifications (109 requirements), 0 failures
@@ -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
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{timecode}
5
- s.version = "0.1.7"
5
+ s.version = "0.1.8"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Julik"]
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: julik-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