timecode 0.1.3 → 0.1.4
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 +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
|