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.
Files changed (4) hide show
  1. data/History.txt +6 -1
  2. data/lib/timecode.rb +12 -22
  3. data/test/test_timecode.rb +121 -19
  4. 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.3'
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. If a string is passed, it will be parsed, an integer
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 self.new(total_or_string = 0, fps = DEFAULT_FPS)
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
- raise NonPositiveFps, "FPS cannot be less than 0" if new_fps < 1
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
- "%02d:%02d:%02d:%02d" % value_parts
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
- # Slice the timespan in pieces
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
- "%02d:%02d:%02d.%02d" % vp
300
+ WITH_FRACTIONS_OF_SECOND % vp
311
301
  end
312
302
  alias_method :with_fractional_seconds, :with_frames_as_fraction
313
303
 
@@ -1,9 +1,6 @@
1
1
  require 'test/unit'
2
- require 'rubygems'
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.seconds * 25
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.minutes * 24
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.seconds * 25) + 5)
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 test_from_seconds
182
- fraction = 7.1
183
- tc = Timecode.from_seconds(fraction, 10)
184
- assert_equal "00:00:07:01", tc.to_s
185
-
186
- fraction = 7.5
187
- tc = Timecode.from_seconds(fraction, 10)
188
- assert_equal "00:00:07:05", tc.to_s
189
-
190
- fraction = 7.16
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.3
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: 2008-12-28 00:00:00 +01:00
12
+ date: 2009-01-01 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency