timecode 0.1.6 → 0.1.7

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.
data/History.txt CHANGED
@@ -1,3 +1,7 @@
1
+ === 0.1.6 / 2009-01-31
2
+
3
+ * Simplified parsing code, more safeguards and needless exceptions removed
4
+
1
5
  === 0.1.6 / 2009-01-28
2
6
 
3
7
  * Fixed RDoc linkage
data/README.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  = timecode
2
2
 
3
- * http://wiretap.rubyforge.org/timecode
3
+ * http://guerilla-di.rubyforge.org/timecode
4
4
 
5
5
  == DESCRIPTION:
6
6
 
data/SPECS.txt CHANGED
@@ -1,8 +1,19 @@
1
1
 
2
- == Timecode on instantiation should
3
- * be instantable from int
2
+ == Timecode.new() should
3
+ * instantiate from int
4
4
  * always coerce FPS to float
5
5
  * create a zero TC with no arguments
6
+ * accept full string SMPTE timecode as well
7
+
8
+ == Timecode.at() 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
+ * propery accept usable values
14
+
15
+ == A new Timecode object should
16
+ * be frozen
6
17
 
7
18
  == An existing Timecode should
8
19
  * report that the framerates are in delta
@@ -13,11 +24,13 @@
13
24
  * support seconds
14
25
  * support frames
15
26
  * report frame_interval as a float
27
+ * be comparable
28
+ * raise on comparison of incompatible timecodes
16
29
 
17
30
  == A Timecode of zero should
18
31
  * properly respond to zero?
19
32
 
20
- == An existing TImecode on inspection should
33
+ == An existing Timecode on inspection should
21
34
  * properly present himself via inspect
22
35
  * properly print itself
23
36
 
@@ -37,26 +50,19 @@
37
50
  * raise when subtracting a Timecode with a different framerate
38
51
  * support multiplication
39
52
  * raise when the resultig Timecode is negative
40
- * yield a Timecode when divided by an Integer
41
- * yield a number when divided by another Timecode
53
+ * return a Timecode when divided by an Integer
54
+ * return a number when divided by another Timecode
42
55
 
43
56
  == A Timecode used with fractional number of seconds
44
57
  * should properly return fractional seconds
45
58
  * properly translate to frames when instantiated from fractional seconds
46
59
 
47
- == Timecode.at() should
48
- * disallow more than 99 hrs
49
- * disallow more than 59 minutes
50
- * disallow more than 59 seconds
51
- * disallow more frames than what the framerate permits
52
- * propery accept usable values
53
-
54
60
  == A custom Timecode descendant should
55
61
  * properly classify on parse
56
62
  * properly classify on at
57
63
  * properly classify on calculations
58
64
 
59
- == Timecode.parse() should
65
+ == Timecode.parse should
60
66
  * handle complete SMPTE timecode
61
67
  * handle complete SMPTE timecode via new
62
68
  * refuse to handle timecode that is out of range for the framerate
@@ -66,7 +72,11 @@
66
72
  * parse a number with m suffix as minutes
67
73
  * parse a number with h suffix as hours
68
74
  * parse different suffixes as a sum of elements
75
+ * parse a number of digits as timecode
76
+ * truncate a large number to the parseable length
77
+ * left-pad a large number to give proper TC
69
78
  * parse timecode with fractional second instead of frames
79
+ * raise when trying to parse DF timecode
70
80
  * raise on improper format
71
81
 
72
82
  == Timecode.soft_parse should
@@ -77,4 +87,4 @@
77
87
  * parse from a 4x4bits packed 32bit unsigned int
78
88
  * properly convert itself back to 4x4 bits 32bit unsigned int
79
89
 
80
- 52 specifications (87 requirements), 0 failures
90
+ 60 specifications (101 requirements), 0 failures
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.6'
15
+ VERSION = '0.1.7'
16
16
 
17
17
  include Comparable
18
18
 
@@ -33,9 +33,6 @@ class Timecode
33
33
  # All Timecode lib errors inherit from this
34
34
  class Error < RuntimeError; end
35
35
 
36
- # Will be raised for functions that are not supported
37
- class TimecodeLibError < Error; end
38
-
39
36
  # Gets raised if timecode is out of range (like 100 hours long)
40
37
  class RangeError < Error; end
41
38
 
@@ -81,18 +78,19 @@ class Timecode
81
78
  # * 00:00:00:00 - will be parsed as zero TC
82
79
  def parse(input, with_fps = DEFAULT_FPS)
83
80
  # Drop frame goodbye
84
- raise Error, "We do not support drop frame" if (input =~ /\;/)
81
+ raise Error, "We do not support drop frame" if (input =~ DF_TC_RE)
85
82
 
86
83
  hrs, mins, secs, frames = 0,0,0,0
87
84
  atoms = []
88
85
 
89
86
  # 00:00:00:00
90
87
  if (input =~ COMPLETE_TC_RE)
91
- atoms = input.scan(COMPLETE_TC_RE).to_a.flatten
88
+ atoms_and_fps = input.scan(COMPLETE_TC_RE).to_a.flatten.map{|e| Integer(e)} + [with_fps]
89
+ return at(*atoms_and_fps)
92
90
  # 00:00:00.0
93
91
  elsif input =~ FRACTIONAL_TC_RE
94
92
  parse_with_fractional_seconds(input, with_fps)
95
- # 10h 20m 10s 1f
93
+ # 10h 20m 10s 1f 00:00:00:01 - space separated is a sum of parts
96
94
  elsif input =~ /\s/
97
95
  return input.split.map{|part| parse(part, with_fps) }.inject { |sum, p| sum + p.total }
98
96
  # 10s
@@ -107,24 +105,16 @@ class Timecode
107
105
  # 60f - 60 frames, or 2 seconds and 10 frames
108
106
  elsif input =~ /^(\d+)f$/i
109
107
  return new(input.to_i, with_fps)
110
- # A bunch of integers
108
+ # Only a bunch of digits, treat 12345 as 00:01:23:45
111
109
  elsif (input =~ /^(\d+)$/)
112
- ints = input.split(//)
113
- atoms.unshift [ints.pop, ints.pop].reverse.join.to_i
114
- atoms.unshift [ints.pop, ints.pop].reverse.join.to_i
115
- atoms.unshift [ints.pop, ints.pop].reverse.join.to_i
116
- atoms.unshift [ints.pop, ints.pop].reverse.join.to_i
117
- else
118
- raise CannotParse, "Cannot parse #{input} into timecode, no match"
119
- end
120
-
121
- if atoms.any?
122
- hrs, mins, secs, frames = atoms.map{|e| e.to_i}
110
+ atoms_len = 2 * 4
111
+ # left-pad input AND truncate if needed
112
+ padded = input[0..atoms_len].rjust(8, "0")
113
+ atoms = padded.scan(/(\d{2})/).flatten.map{|e| e.to_i } + [with_fps]
114
+ return at(*atoms)
123
115
  else
124
- raise CannotParse, "Cannot parse #{input} into timecode, atoms were empty"
116
+ raise CannotParse, "Cannot parse #{input} into timecode, unknown format"
125
117
  end
126
-
127
- at(hrs, mins, secs, frames, with_fps)
128
118
  end
129
119
 
130
120
  # Initialize a Timecode object at this specfic timecode
@@ -290,10 +280,10 @@ class Timecode
290
280
 
291
281
  # Timecodes can be compared to each other
292
282
  def <=>(other_tc)
293
- if other_tc.is_a?(Timecode) && framerate_in_delta(fps, other_tc.fps)
283
+ if framerate_in_delta(fps, other_tc.fps)
294
284
  self.total <=> other_tc.total
295
- else
296
- self.total <=> other_tc
285
+ else
286
+ raise WrongFramerate, "Cannot compare timecodes with different framerates"
297
287
  end
298
288
  end
299
289
 
@@ -5,9 +5,9 @@ require 'test/spec'
5
5
  require File.dirname(__FILE__) + '/../lib/timecode'
6
6
 
7
7
 
8
- context "Timecode on instantiation should" do
8
+ context "Timecode.new() should" do
9
9
 
10
- specify "be instantable from int" do
10
+ specify "instantiate from int" do
11
11
  tc = Timecode.new(10)
12
12
  tc.should.be.kind_of Timecode
13
13
  tc.total.should.equal 10
@@ -21,6 +21,41 @@ context "Timecode on instantiation should" do
21
21
  specify "create a zero TC with no arguments" do
22
22
  Timecode.new(nil).should.be.zero?
23
23
  end
24
+
25
+ specify "accept full string SMPTE timecode as well" do
26
+ Timecode.new("00:25:30:10", 25).should.equal Timecode.parse("00:25:30:10")
27
+ end
28
+ end
29
+
30
+ context "Timecode.at() should" do
31
+
32
+ specify "disallow more than 99 hrs" do
33
+ lambda{ Timecode.at(99,0,0,0) }.should.not.raise
34
+ lambda{ Timecode.at(100,0,0,0) }.should.raise(Timecode::RangeError)
35
+ end
36
+
37
+ specify "disallow more than 59 minutes" do
38
+ lambda{ Timecode.at(1,60,0,0) }.should.raise(Timecode::RangeError)
39
+ end
40
+
41
+ specify "disallow more than 59 seconds" do
42
+ lambda{ Timecode.at(1,0,60,0) }.should.raise(Timecode::RangeError)
43
+ end
44
+
45
+ specify "disallow more frames than what the framerate permits" do
46
+ lambda{ Timecode.at(1,0,60,25, 25) }.should.raise(Timecode::RangeError)
47
+ lambda{ Timecode.at(1,0,60,32, 30) }.should.raise(Timecode::RangeError)
48
+ end
49
+
50
+ specify "propery accept usable values" do
51
+ Timecode.at(20, 20, 10, 5).to_s.should.equal "20:20:10:05"
52
+ end
53
+ end
54
+
55
+ context "A new Timecode object should" do
56
+ specify "be frozen" do
57
+ Timecode.new(10).should.be.frozen
58
+ end
24
59
  end
25
60
 
26
61
  context "An existing Timecode should" do
@@ -31,7 +66,6 @@ context "An existing Timecode should" do
31
66
  @film_tc = Timecode.new(@one_and_a_half_film, 24)
32
67
  end
33
68
 
34
-
35
69
  specify "report that the framerates are in delta" do
36
70
  tc = Timecode.new(1)
37
71
  tc.framerate_in_delta(25.0000000000000001, 25.0000000000000003).should.equal(true)
@@ -77,6 +111,15 @@ context "An existing Timecode should" do
77
111
  tc.frame_interval.should.be.close 0.03333, 0.0001
78
112
  end
79
113
 
114
+ specify "be comparable" do
115
+ (Timecode.new(10) < Timecode.new(9)).should.equal false
116
+ (Timecode.new(9) < Timecode.new(10)).should.equal true
117
+ Timecode.new(9).should.equal Timecode.new(9)
118
+ end
119
+
120
+ specify "raise on comparison of incompatible timecodes" do
121
+ lambda { Timecode.new(10, 10) < Timecode.new(10, 20)}.should.raise(Timecode::WrongFramerate)
122
+ end
80
123
  end
81
124
 
82
125
  context "A Timecode of zero should" do
@@ -87,9 +130,10 @@ context "A Timecode of zero should" do
87
130
  end
88
131
  end
89
132
 
90
- context "An existing TImecode on inspection should" do
133
+ context "An existing Timecode on inspection should" do
91
134
  specify "properly present himself via inspect" do
92
135
  Timecode.new(10, 25).inspect.should.equal "#<Timecode:00:00:00:10 (10F@25.00)>"
136
+ Timecode.new(10, 12).inspect.should.equal "#<Timecode:00:00:00:10 (10F@12.00)>"
93
137
  end
94
138
 
95
139
  specify "properly print itself" do
@@ -100,7 +144,7 @@ end
100
144
  context "An existing Timecode used within ranges should" do
101
145
  specify "properly provide successive value that is one frame up" do
102
146
  Timecode.new(10).succ.total.should.equal 11
103
- Timecode.new(22).succ.should.equal Timecode.new(23)
147
+ Timecode.new(22, 45).succ.should.equal Timecode.new(23, 45)
104
148
  end
105
149
 
106
150
  specify "work as a range member" do
@@ -155,13 +199,13 @@ context "A Timecode on calculations should" do
155
199
  lambda { Timecode.new(10) * -200 }.should.raise(Timecode::RangeError)
156
200
  end
157
201
 
158
- specify "yield a Timecode when divided by an Integer" do
202
+ specify "return a Timecode when divided by an Integer" do
159
203
  v = Timecode.new(200) / 20
160
204
  v.should.be.kind_of(Timecode)
161
205
  v.should.equal Timecode.new(10)
162
206
  end
163
207
 
164
- specify "yield a number when divided by another Timecode" do
208
+ specify "return a number when divided by another Timecode" do
165
209
  v = Timecode.new(200) / Timecode.new(20)
166
210
  v.should.be.kind_of(Numeric)
167
211
  v.should.equal 10
@@ -194,31 +238,6 @@ context "A Timecode used with fractional number of seconds" do
194
238
 
195
239
  end
196
240
 
197
- context "Timecode.at() should" do
198
-
199
- specify "disallow more than 99 hrs" do
200
- lambda{ Timecode.at(99,0,0,0) }.should.not.raise
201
- lambda{ Timecode.at(100,0,0,0) }.should.raise(Timecode::RangeError)
202
- end
203
-
204
- specify "disallow more than 59 minutes" do
205
- lambda{ Timecode.at(1,60,0,0) }.should.raise(Timecode::RangeError)
206
- end
207
-
208
- specify "disallow more than 59 seconds" do
209
- lambda{ Timecode.at(1,0,60,0) }.should.raise(Timecode::RangeError)
210
- end
211
-
212
- specify "disallow more frames than what the framerate permits" do
213
- lambda{ Timecode.at(1,0,60,25, 25) }.should.raise(Timecode::RangeError)
214
- lambda{ Timecode.at(1,0,60,32, 30) }.should.raise(Timecode::RangeError)
215
- end
216
-
217
- specify "propery accept usable values" do
218
- Timecode.at(20, 20, 10, 5).to_s.should.equal "20:20:10:05"
219
- end
220
- end
221
-
222
241
  context "A custom Timecode descendant should" do
223
242
  class CustomTC < Timecode; end
224
243
 
@@ -246,7 +265,7 @@ context "A custom Timecode descendant should" do
246
265
 
247
266
  end
248
267
 
249
- context "Timecode.parse() should" do
268
+ context "Timecode.parse should" do
250
269
 
251
270
  specify "handle complete SMPTE timecode" do
252
271
  simple_tc = "00:10:34:10"
@@ -290,6 +309,21 @@ context "Timecode.parse() should" do
290
309
  Timecode.parse("1h 4f").to_s.should.equal '01:00:00:04'
291
310
  Timecode.parse("4f 1h").to_s.should.equal '01:00:00:04'
292
311
  Timecode.parse("29f 1h").to_s.should.equal '01:00:01:04'
312
+ Timecode.parse("29f \n\n\n\n\n\ 1h").to_s.should.equal '01:00:01:04'
313
+ end
314
+
315
+ specify "parse a number of digits as timecode" do
316
+ Timecode.parse("00000001").to_s.should.equal "00:00:00:01"
317
+ Timecode.parse("1").to_s.should.equal "00:00:00:01"
318
+ Timecode.parse("10").to_s.should.equal "00:00:00:10"
319
+ end
320
+
321
+ specify "truncate a large number to the parseable length" do
322
+ Timecode.parse("1000000000000000001").to_s.should.equal "10:00:00:00"
323
+ end
324
+
325
+ specify "left-pad a large number to give proper TC" do
326
+ Timecode.parse("123456", 57).to_s.should.equal "00:12:34:56"
293
327
  end
294
328
 
295
329
  specify "parse timecode with fractional second instead of frames" do
@@ -310,6 +344,11 @@ context "Timecode.parse() should" do
310
344
  tc.to_s.should.equal "00:00:07:02"
311
345
  end
312
346
 
347
+ specify "raise when trying to parse DF timecode" do
348
+ df_tc = "00:00:00;01"
349
+ lambda { Timecode.parse(df_tc)}.should.raise(Timecode::Error)
350
+ end
351
+
313
352
  specify "raise on improper format" do
314
353
  lambda { Timecode.parse("Meaningless nonsense", 25) }.should.raise Timecode::CannotParse
315
354
  lambda { Timecode.parse("", 25) }.should.raise Timecode::CannotParse
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.6
4
+ version: 0.1.7
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-28 00:00:00 +01:00
12
+ date: 2009-01-31 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -30,7 +30,7 @@ dependencies:
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 1.8.2
33
+ version: 1.8.3
34
34
  version:
35
35
  description: Value class for SMPTE timecode information
36
36
  email:
@@ -53,7 +53,7 @@ files:
53
53
  - lib/timecode.rb
54
54
  - test/test_timecode.rb
55
55
  has_rdoc: true
56
- homepage: http://wiretap.rubyforge.org/timecode
56
+ homepage: http://guerilla-di.rubyforge.org/timecode
57
57
  post_install_message:
58
58
  rdoc_options:
59
59
  - --main