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 +4 -0
- data/README.txt +1 -1
- data/SPECS.txt +24 -14
- data/lib/timecode.rb +15 -25
- data/test/test_timecode.rb +72 -33
- metadata +4 -4
data/History.txt
CHANGED
data/README.txt
CHANGED
data/SPECS.txt
CHANGED
@@ -1,8 +1,19 @@
|
|
1
1
|
|
2
|
-
== Timecode
|
3
|
-
*
|
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
|
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
|
-
*
|
41
|
-
*
|
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
|
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
|
-
|
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.
|
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
|
-
|
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
|
-
#
|
108
|
+
# Only a bunch of digits, treat 12345 as 00:01:23:45
|
111
109
|
elsif (input =~ /^(\d+)$/)
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
atoms
|
116
|
-
atoms
|
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,
|
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
|
283
|
+
if framerate_in_delta(fps, other_tc.fps)
|
294
284
|
self.total <=> other_tc.total
|
295
|
-
else
|
296
|
-
|
285
|
+
else
|
286
|
+
raise WrongFramerate, "Cannot compare timecodes with different framerates"
|
297
287
|
end
|
298
288
|
end
|
299
289
|
|
data/test/test_timecode.rb
CHANGED
@@ -5,9 +5,9 @@ require 'test/spec'
|
|
5
5
|
require File.dirname(__FILE__) + '/../lib/timecode'
|
6
6
|
|
7
7
|
|
8
|
-
context "Timecode
|
8
|
+
context "Timecode.new() should" do
|
9
9
|
|
10
|
-
specify "
|
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
|
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 "
|
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 "
|
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
|
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.
|
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-
|
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.
|
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://
|
56
|
+
homepage: http://guerilla-di.rubyforge.org/timecode
|
57
57
|
post_install_message:
|
58
58
|
rdoc_options:
|
59
59
|
- --main
|