julik-timecode 0.1.7 → 0.1.8

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.
@@ -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