timecode 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +6 -1
- data/lib/timecode.rb +12 -22
- data/test/test_timecode.rb +121 -19
- metadata +2 -2
data/History.txt
CHANGED
@@ -1,6 +1,11 @@
|
|
1
|
+
=== 0.1.4 / 2000-01-01
|
2
|
+
|
3
|
+
* Expanded test coverage
|
4
|
+
* Some formatting/doc improvements
|
5
|
+
|
1
6
|
=== 0.1.3 / 2008-12-25
|
2
7
|
|
3
|
-
* Implement the format FFmpeg uses
|
8
|
+
* Implement the format FFmpeg uses (fractional seconds instead of frames)
|
4
9
|
|
5
10
|
=== 0.1.2 / 2008-12-25
|
6
11
|
|
data/lib/timecode.rb
CHANGED
@@ -12,13 +12,15 @@
|
|
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.4'
|
16
16
|
|
17
17
|
include Comparable
|
18
18
|
|
19
19
|
DEFAULT_FPS = 25.0
|
20
20
|
ALLOWED_FPS_DELTA = 0.001
|
21
21
|
COMPLETE_TC_RE = /^(\d{1,2}):(\d{1,2}):(\d{1,2}):(\d{1,2})$/
|
22
|
+
WITH_FRACTIONS_OF_SECOND = "%02d:%02d:%02d.%02d"
|
23
|
+
WITH_FRAMES = "%02d:%02d:%02d:%02d"
|
22
24
|
|
23
25
|
# All Timecode lib errors inherit from this
|
24
26
|
class Error < RuntimeError; end
|
@@ -44,19 +46,9 @@ class Timecode
|
|
44
46
|
# Well well...
|
45
47
|
class MethodRequiresTimecode < ArgumentError; end
|
46
48
|
|
47
|
-
# Initialize a new Timecode
|
49
|
+
# Initialize a new Timecode object with a certain amount of frames and a framerate
|
48
50
|
# will be interpreted as the total number of frames
|
49
|
-
def
|
50
|
-
if total_or_string.nil?
|
51
|
-
new(0, fps)
|
52
|
-
elsif total_or_string.is_a?(String)
|
53
|
-
parse(total_or_string, fps)
|
54
|
-
else
|
55
|
-
super(total_or_string, fps)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def initialize(total = 0, fps = DEFAULT_FPS) # :nodoc:
|
51
|
+
def initialize(total = 0, fps = DEFAULT_FPS)
|
60
52
|
raise RangeError, "Timecode cannot be negative" if total.to_f < 0
|
61
53
|
raise WrongFramerate, "FPS cannot be zero" if fps.zero?
|
62
54
|
|
@@ -188,7 +180,7 @@ class Timecode
|
|
188
180
|
to_f
|
189
181
|
end
|
190
182
|
|
191
|
-
#get FPS
|
183
|
+
# get FPS
|
192
184
|
def fps
|
193
185
|
@fps
|
194
186
|
end
|
@@ -231,16 +223,13 @@ class Timecode
|
|
231
223
|
# Convert to different framerate based on the total frames. Therefore,
|
232
224
|
# 1 second of PAL video will convert to 25 frames of NTSC (this
|
233
225
|
# is suitable for PAL to film TC conversions and back).
|
234
|
-
# It does not account for pulldown or anything in that sense, because
|
235
|
-
# then you need to think about cadences and such
|
236
226
|
def convert(new_fps)
|
237
|
-
|
238
|
-
self.class.new((total/fps)*new_fps, new_fps)
|
227
|
+
self.class.new(@total, new_fps)
|
239
228
|
end
|
240
229
|
|
241
230
|
# get formatted SMPTE timecode
|
242
231
|
def to_s
|
243
|
-
|
232
|
+
WITH_FRAMES % value_parts
|
244
233
|
end
|
245
234
|
|
246
235
|
# get total frames as float
|
@@ -286,9 +275,10 @@ class Timecode
|
|
286
275
|
self.class.new(@total + 1, @fps)
|
287
276
|
end
|
288
277
|
|
289
|
-
#
|
278
|
+
# Get the number of times a passed timecode fits into this time span (if performed with Timecode) or
|
279
|
+
# a Timecode that multiplied by arg will give this one
|
290
280
|
def /(arg)
|
291
|
-
Timecode.new(@total/arg, @fps)
|
281
|
+
arg.is_a?(Timecode) ? (@total / arg.total) : Timecode.new(@total /arg, @fps)
|
292
282
|
end
|
293
283
|
|
294
284
|
# Timecodes can be compared to each other
|
@@ -307,7 +297,7 @@ class Timecode
|
|
307
297
|
def with_frames_as_fraction
|
308
298
|
vp = value_parts.dup
|
309
299
|
vp[-1] = (100.0 / @fps) * vp[-1]
|
310
|
-
|
300
|
+
WITH_FRACTIONS_OF_SECOND % vp
|
311
301
|
end
|
312
302
|
alias_method :with_fractional_seconds, :with_frames_as_fraction
|
313
303
|
|
data/test/test_timecode.rb
CHANGED
@@ -1,9 +1,6 @@
|
|
1
1
|
require 'test/unit'
|
2
|
-
require '
|
3
|
-
require 'timecode'
|
2
|
+
require File.dirname(__FILE__) + '/../lib/timecode'
|
4
3
|
|
5
|
-
# for Fixnum#hours
|
6
|
-
require 'active_support'
|
7
4
|
|
8
5
|
class TimecodeTest < Test::Unit::TestCase
|
9
6
|
|
@@ -29,6 +26,10 @@ class TimecodeTest < Test::Unit::TestCase
|
|
29
26
|
assert t1 == t2
|
30
27
|
end
|
31
28
|
|
29
|
+
def test_to_i_is_total
|
30
|
+
assert_equal 10, Timecode.new(10).to_i
|
31
|
+
end
|
32
|
+
|
32
33
|
def test_inspect
|
33
34
|
tc = Timecode.new(10, 25)
|
34
35
|
assert_equal "#<Timecode:00:00:00:10 (10F@25.00)>", tc.inspect
|
@@ -39,7 +40,8 @@ class TimecodeTest < Test::Unit::TestCase
|
|
39
40
|
end
|
40
41
|
|
41
42
|
def test_basics
|
42
|
-
five_seconds_of_pal = 5
|
43
|
+
five_seconds_of_pal = 5 * 25
|
44
|
+
|
43
45
|
tc = Timecode.new(five_seconds_of_pal, 25)
|
44
46
|
assert_equal 0, tc.hours
|
45
47
|
assert_equal 0, tc.minutes
|
@@ -48,7 +50,7 @@ class TimecodeTest < Test::Unit::TestCase
|
|
48
50
|
assert_equal five_seconds_of_pal, tc.total
|
49
51
|
assert_equal "00:00:05:00", tc.to_s
|
50
52
|
|
51
|
-
one_and_a_half_hour_of_hollywood = 90
|
53
|
+
one_and_a_half_hour_of_hollywood = (90 * 60) * 24
|
52
54
|
|
53
55
|
film_tc = Timecode.new(one_and_a_half_hour_of_hollywood, 24)
|
54
56
|
assert_equal 1, film_tc.hours
|
@@ -65,7 +67,7 @@ class TimecodeTest < Test::Unit::TestCase
|
|
65
67
|
tc + film_tc
|
66
68
|
end
|
67
69
|
|
68
|
-
two_seconds_and_five_frames_of_pal = ((2
|
70
|
+
two_seconds_and_five_frames_of_pal = ((2 * 25) + 5)
|
69
71
|
pal_tc = Timecode.new(two_seconds_and_five_frames_of_pal, 25)
|
70
72
|
assert_nothing_raised do
|
71
73
|
added_tc = pal_tc + tc
|
@@ -80,15 +82,81 @@ class TimecodeTest < Test::Unit::TestCase
|
|
80
82
|
|
81
83
|
def test_zero
|
82
84
|
assert Timecode.new(0).zero?
|
85
|
+
assert Timecode.parse("00:00:00:00").zero?
|
83
86
|
assert !Timecode.new(1).zero?
|
84
87
|
assert !Timecode.new(1000).zero?
|
85
88
|
end
|
86
89
|
|
90
|
+
def test_timecode_rangeable
|
91
|
+
r = Timecode.new(10)...Timecode.new(20)
|
92
|
+
assert_equal 10, r.to_a.length
|
93
|
+
assert_equal Timecode.new(14), r.to_a[4]
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_frame_interval
|
97
|
+
tc = Timecode.new(10)
|
98
|
+
assert_in_delta tc.frame_interval, 0.04, 0.0001
|
99
|
+
|
100
|
+
tc = Timecode.new(10, 30)
|
101
|
+
assert_in_delta tc.frame_interval, 0.03333, 0.0001
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
class ConversionTest < Test::Unit::TestCase
|
106
|
+
def test_convert_25_at_24
|
107
|
+
tc = Timecode.new(40, 25)
|
108
|
+
at24 = tc.convert(24)
|
109
|
+
assert_equal tc.total, at24.total
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
class CalculationsTest < Test::Unit::TestCase
|
87
114
|
def test_plus
|
88
115
|
a, b = Timecode.new(24, 25.000000000000001), Timecode.new(22, 25.000000000000002)
|
89
116
|
assert_equal Timecode.new(24 + 22, 25.000000000000001), (a + b)
|
90
117
|
end
|
91
118
|
|
119
|
+
def test_plus_with_different_framerates_should_raise
|
120
|
+
assert_raise(Timecode::WrongFramerate) { Timecode.new(10, 25) + Timecode.new(10, 30) }
|
121
|
+
end
|
122
|
+
|
123
|
+
def test_plus_with_int_gives_a_timecode
|
124
|
+
assert_equal Timecode.new(10), Timecode.new(5) + 5
|
125
|
+
end
|
126
|
+
|
127
|
+
def test_min
|
128
|
+
a, b = Timecode.new(10), Timecode.new(4)
|
129
|
+
assert_equal Timecode.new(6), a - b
|
130
|
+
end
|
131
|
+
|
132
|
+
def test_min_with_int_gives_a_timecode
|
133
|
+
assert_equal Timecode.new(10), Timecode.new(15) - 5
|
134
|
+
end
|
135
|
+
|
136
|
+
def test_min_with_different_framerates_should_raise
|
137
|
+
assert_raise(Timecode::WrongFramerate) { Timecode.new(10, 25) - Timecode.new(10, 30) }
|
138
|
+
end
|
139
|
+
|
140
|
+
def test_mult
|
141
|
+
assert_equal Timecode.new(100), Timecode.new(10) * 10
|
142
|
+
end
|
143
|
+
|
144
|
+
def test_mult_by_neg_number_should_raise
|
145
|
+
assert_raise(Timecode::RangeError) { Timecode.new(10) * -200 }
|
146
|
+
end
|
147
|
+
|
148
|
+
def test_div_by_int_gives_a_timecode
|
149
|
+
v = Timecode.new(200) / 20
|
150
|
+
assert_kind_of Timecode, v
|
151
|
+
assert_equal Timecode.new(10), v
|
152
|
+
end
|
153
|
+
|
154
|
+
def test_div_by_timecode_gives_an_int
|
155
|
+
v = Timecode.new(200) / Timecode.new(20)
|
156
|
+
assert_kind_of Numeric, v
|
157
|
+
assert_equal 10, v
|
158
|
+
end
|
159
|
+
|
92
160
|
def test_tc_with_frames_as_fraction
|
93
161
|
tc = Timecode.new(100 -1, fps = 25)
|
94
162
|
assert_equal 24, tc.frames
|
@@ -100,7 +168,43 @@ class TimecodeTest < Test::Unit::TestCase
|
|
100
168
|
tc = Timecode.new(25, 12.5)
|
101
169
|
assert_equal "00:00:02:00", tc.to_s
|
102
170
|
end
|
171
|
+
|
172
|
+
def test_from_seconds
|
173
|
+
fraction = 7.1
|
174
|
+
tc = Timecode.from_seconds(fraction, 10)
|
175
|
+
assert_equal "00:00:07:01", tc.to_s
|
176
|
+
|
177
|
+
fraction = 7.5
|
178
|
+
tc = Timecode.from_seconds(fraction, 10)
|
179
|
+
assert_equal "00:00:07:05", tc.to_s
|
180
|
+
|
181
|
+
fraction = 7.16
|
182
|
+
tc = Timecode.from_seconds(fraction, 12.5)
|
183
|
+
assert_equal "00:00:07:02", tc.to_s
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
187
|
+
|
188
|
+
class TestAt < Test::Unit::TestCase
|
103
189
|
|
190
|
+
def test_at_disallows_more_than_99_hrs
|
191
|
+
assert_nothing_raised { Timecode.at(99,0,0,0) }
|
192
|
+
assert_raise(Timecode::RangeError) { Timecode.at(100,0,0,0) }
|
193
|
+
end
|
194
|
+
|
195
|
+
def test_at_disallows_more_than_59_mins
|
196
|
+
assert_raise(Timecode::RangeError) { Timecode.at(1,60,0,0) }
|
197
|
+
end
|
198
|
+
|
199
|
+
def test_at_disallows_more_than_59_secs
|
200
|
+
assert_raise(Timecode::RangeError) { Timecode.at(1,0,60,0) }
|
201
|
+
end
|
202
|
+
|
203
|
+
def test_at_disallows_more_frames_than_framerate
|
204
|
+
assert_raise(Timecode::RangeError) { Timecode.at(1,0,60,25, 25) }
|
205
|
+
assert_raise(Timecode::RangeError) { Timecode.at(1,0,60,32, 30) }
|
206
|
+
end
|
207
|
+
|
104
208
|
end
|
105
209
|
|
106
210
|
class TestParsing < Test::Unit::TestCase
|
@@ -178,18 +282,16 @@ class TestParsing < Test::Unit::TestCase
|
|
178
282
|
# assert_equal Timecode.new(17), tc
|
179
283
|
# end
|
180
284
|
|
181
|
-
def
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
tc = Timecode.from_seconds(fraction, 12.5)
|
192
|
-
assert_equal "00:00:07:02", tc.to_s
|
285
|
+
def test_soft_parse_does_not_raise_and_createz_zeroed_tc
|
286
|
+
assert_nothing_raised do
|
287
|
+
tc = Timecode.soft_parse("Meaningless nonsense", 25)
|
288
|
+
assert tc.zero?
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
def test_parse_raoses_on_improper_format
|
293
|
+
assert_raise(Timecode::CannotParse) { Timecode.parse("Meaningless nonsense", 25) }
|
294
|
+
assert_raise(Timecode::CannotParse) { Timecode.parse("", 25) }
|
193
295
|
end
|
194
296
|
end
|
195
297
|
|
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.4
|
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:
|
12
|
+
date: 2009-01-01 00:00:00 +01:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|