timecode 0.2.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.DS_Store ADDED
Binary file
data/.gemtest ADDED
File without changes
data/History.txt CHANGED
@@ -1,3 +1,15 @@
1
+ === 1.1.0 / 2011-02-16
2
+
3
+ * Added timecode with ticks support, used by CineCanvas (wolfgangw)
4
+
5
+ === 1.0.0 / 2011-02-16
6
+
7
+ * After all these years we consider Timecode stable
8
+
9
+ === 0.3.0 / 2010-10-25
10
+
11
+ * Let Timecode.parse and Timecode#to_s handle 24fps timecode with a plus sign (00:00:00+10)
12
+
1
13
  === 0.2.1 / 2010-05-11
2
14
 
3
15
  * Add Timecode#coerce and Timecode#adjacent_to?
data/Manifest.txt CHANGED
@@ -1,7 +1,8 @@
1
+ .DS_Store
1
2
  History.txt
2
3
  Manifest.txt
3
- README.txt
4
- SPECS.txt
4
+ README.rdoc
5
5
  Rakefile
6
+ SPECS.rdoc
6
7
  lib/timecode.rb
7
- test/test_timecode.rb
8
+ test/test_timecode.rb
File without changes
data/Rakefile CHANGED
@@ -4,13 +4,15 @@ require './lib/timecode.rb'
4
4
 
5
5
  Hoe.spec('timecode') do |p|
6
6
  p.version = Timecode::VERSION
7
+ p.readme_file = 'README.rdoc'
8
+ p.extra_rdoc_files = FileList['*.rdoc']
9
+
7
10
  p.developer('Julik', 'me@julik.nl')
8
- p.extra_deps.reject! {|e| e[0] == 'hoe' }
9
- p.extra_deps << ['test-spec', '>=0']
11
+ p.extra_dev_deps = {"test-spec" => ">=0"}
10
12
  p.rubyforge_name = 'guerilla-di'
11
13
  p.remote_rdoc_dir = 'timecode'
12
14
  end
13
15
 
14
16
  task "specs" do
15
- `specrb test/* --rdox > SPECS.txt`
17
+ `specrb test/* --rdox > SPECS.rdoc`
16
18
  end
@@ -85,7 +85,11 @@
85
85
 
86
86
  == Timecode.parse should
87
87
  * handle complete SMPTE timecode
88
+ * handle complete SMPTE timecode with plus for 24 frames per second
88
89
  * handle timecode with fractional seconds
90
+ * handle timecode with ticks
91
+ * raise when there are more than 249 ticks
92
+ * handle timecode with fractional seconds with spaces at start and end
89
93
  * parse a row of numbers as parts of a timecode starting from the right
90
94
  * parse a number with f suffix as frames
91
95
  * parse a number with s suffix as seconds
@@ -109,4 +113,4 @@
109
113
  * parse from a 4x4bits packed 32bit unsigned int
110
114
  * properly convert itself back to 4x4 bits 32bit unsigned int
111
115
 
112
- 74 specifications (118 requirements), 0 failures
116
+ 78 specifications (124 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.2.1'
15
+ VERSION = '1.1.0'
16
16
 
17
17
  include Comparable
18
18
 
@@ -20,14 +20,19 @@ class Timecode
20
20
 
21
21
  #:stopdoc:
22
22
  NTSC_FPS = (30.0 * 1000 / 1001).freeze
23
+ FILMSYNC_FPS = (24.0 * 1000 / 1001).freeze
23
24
  ALLOWED_FPS_DELTA = (0.001).freeze
24
25
 
25
26
  COMPLETE_TC_RE = /^(\d{2}):(\d{2}):(\d{2}):(\d{2})$/
27
+ COMPLETE_TC_RE_24 = /^(\d{2}):(\d{2}):(\d{2})\+(\d{2})$/
26
28
  DF_TC_RE = /^(\d{1,2}):(\d{1,2}):(\d{1,2});(\d{2})$/
27
- FRACTIONAL_TC_RE = /^(\d{2}):(\d{2}):(\d{2}).(\d{1,8})$/
29
+ FRACTIONAL_TC_RE = /^(\d{2}):(\d{2}):(\d{2})\.(\d{1,8})$/
30
+ TICKS_TC_RE = /^(\d{2}):(\d{2}):(\d{2}):(\d{3})$/
28
31
 
29
32
  WITH_FRACTIONS_OF_SECOND = "%02d:%02d:%02d.%02d"
30
33
  WITH_FRAMES = "%02d:%02d:%02d:%02d"
34
+ WITH_FRAMES_24 = "%02d:%02d:%02d+%02d"
35
+
31
36
  #:startdoc:
32
37
 
33
38
  # All Timecode lib errors inherit from this
@@ -76,7 +81,9 @@ class Timecode
76
81
  # * 10h 20m 10s 1f (or any combination thereof) - will be disassembled to hours, frames, seconds and so on automatically
77
82
  # * 123 - will be parsed as 00:00:01:23
78
83
  # * 00:00:00:00 - will be parsed as zero TC
79
- def parse(input, with_fps = DEFAULT_FPS)
84
+ def parse(spaced_input, with_fps = DEFAULT_FPS)
85
+ input = spaced_input.strip
86
+
80
87
  # Drop frame goodbye
81
88
  if (input =~ DF_TC_RE)
82
89
  raise Error, "We do not support drop-frame TC"
@@ -84,9 +91,16 @@ class Timecode
84
91
  elsif (input =~ COMPLETE_TC_RE)
85
92
  atoms_and_fps = input.scan(COMPLETE_TC_RE).to_a.flatten.map{|e| e.to_i} + [with_fps]
86
93
  return at(*atoms_and_fps)
94
+ # 00:00:00+00
95
+ elsif (input =~ COMPLETE_TC_RE_24)
96
+ atoms_and_fps = input.scan(COMPLETE_TC_RE_24).to_a.flatten.map{|e| e.to_i} + [24]
97
+ return at(*atoms_and_fps)
87
98
  # 00:00:00.0
88
99
  elsif input =~ FRACTIONAL_TC_RE
89
100
  parse_with_fractional_seconds(input, with_fps)
101
+ # 00:00:00:000
102
+ elsif input =~ TICKS_TC_RE
103
+ parse_with_ticks(input, with_fps)
90
104
  # 10h 20m 10s 1f 00:00:00:01 - space separated is a sum of parts
91
105
  elsif input =~ /\s/
92
106
  parts = input.gsub(/\s/, ' ').split.reject{|e| e.strip.empty? }
@@ -150,6 +164,22 @@ class Timecode
150
164
 
151
165
  parse(tc_with_frameno, fps)
152
166
  end
167
+
168
+ # Parse a timecode with ticks of a second instead of frames. A 'tick' is defined as
169
+ # 4 msec and has a range of 0 to 249. This format can show up in subtitle files for digital cinema
170
+ # used by CineCanvas systems
171
+ def parse_with_ticks(tc_with_ticks, fps = DEFAULT_FPS)
172
+ ticks_expr = /(\d{3})$/
173
+ num_ticks = tc_with_ticks.scan(ticks_expr).to_s.to_i
174
+
175
+ raise RangeError, "Invalid tick count #{num_ticks}" if num_ticks > 249
176
+
177
+ seconds_per_frame = 1.0 / fps
178
+ frame_idx = ( (num_ticks * 0.004) / seconds_per_frame ).floor
179
+ tc_with_frameno = tc_with_ticks.gsub(ticks_expr, "%02d" % frame_idx)
180
+
181
+ parse(tc_with_frameno, fps)
182
+ end
153
183
 
154
184
  # create a timecode from the number of seconds. This is how current time is supplied by
155
185
  # QuickTime and other systems which have non-frame-based timescales
@@ -248,7 +278,11 @@ class Timecode
248
278
 
249
279
  # get formatted SMPTE timecode
250
280
  def to_s
251
- WITH_FRAMES % value_parts
281
+ if (framerate_in_delta(fps, 24))
282
+ WITH_FRAMES_24 % value_parts
283
+ else
284
+ WITH_FRAMES % value_parts
285
+ end
252
286
  end
253
287
 
254
288
  # get total frames as float
@@ -352,4 +386,4 @@ class Timecode
352
386
  @value ||= validate!
353
387
  end
354
388
 
355
- end
389
+ end
@@ -297,7 +297,7 @@ context "A Timecode used with fractional number of seconds" do
297
297
  tc.with_frames_as_fraction.should.equal "00:00:03.96"
298
298
  tc.with_fractional_seconds.should.equal "00:00:03.96"
299
299
  end
300
-
300
+
301
301
  specify "properly translate to frames when instantiated from fractional seconds" do
302
302
  fraction = 7.1
303
303
  tc = Timecode.from_seconds(fraction, 10)
@@ -348,11 +348,44 @@ context "Timecode.parse should" do
348
348
  Timecode.parse(simple_tc).to_s.should.equal(simple_tc)
349
349
  end
350
350
 
351
+ specify "handle complete SMPTE timecode with plus for 24 frames per second" do
352
+ simple_tc = "00:10:34+10"
353
+ p = Timecode.parse(simple_tc)
354
+ p.to_s.should.equal(simple_tc)
355
+ p.fps.should.equal 24
356
+ end
357
+
351
358
  specify "handle timecode with fractional seconds" do
352
359
  tc = Timecode.parse("10:10:10.2", 25)
353
360
  tc.to_s.should.equal "10:10:10:05"
354
361
  end
355
362
 
363
+ specify "handle timecode with ticks" do
364
+ tc = Timecode.parse("10:10:10:103", 25)
365
+ tc.to_s.should.equal "10:10:10:10"
366
+
367
+ tc = Timecode.parse("10:10:10:249", 25)
368
+ tc.to_s.should.equal "10:10:10:24"
369
+ end
370
+
371
+ specify "raise when there are more than 249 ticks" do
372
+ lambda {
373
+ tc = Timecode.parse("10:10:10:250", 25)
374
+ }.should.raise(Timecode::RangeError)
375
+ end
376
+
377
+ specify "handle timecode with fractional seconds with spaces at start and end" do
378
+ tc = Timecode.parse(" 00:00:01.040 ")
379
+ tc.to_s.should.equal "00:00:01:01"
380
+ end
381
+
382
+ # I am commenting this one out for now, these were present in some odd subtitle file.
383
+ # What we probably need is a way for Timecode to "extract" timecodes from a chunk of text.
384
+ # specify "handle timecode with fractional seconds with weirdo UTF spaces at start and end" do
385
+ # tc = Timecode.parse("00:00:01.040")
386
+ # tc.to_s.should.equal "00:00:01:01"
387
+ # end
388
+
356
389
  specify "parse a row of numbers as parts of a timecode starting from the right" do
357
390
  Timecode.parse("10").should.equal Timecode.new(10)
358
391
  Timecode.parse("210").should.equal Timecode.new(60)
metadata CHANGED
@@ -1,7 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: timecode
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ hash: 19
5
+ prerelease:
6
+ segments:
7
+ - 1
8
+ - 1
9
+ - 0
10
+ version: 1.1.0
5
11
  platform: ruby
6
12
  authors:
7
13
  - Julik
@@ -9,39 +15,39 @@ autorequire:
9
15
  bindir: bin
10
16
  cert_chain: []
11
17
 
12
- date: 2010-05-11 00:00:00 +02:00
18
+ date: 2011-05-15 00:00:00 +02:00
13
19
  default_executable:
14
20
  dependencies:
15
21
  - !ruby/object:Gem::Dependency
16
22
  name: test-spec
17
- type: :runtime
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
20
26
  requirements:
21
27
  - - ">="
22
28
  - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
23
32
  version: "0"
24
- version:
25
- - !ruby/object:Gem::Dependency
26
- name: rubyforge
27
33
  type: :development
28
- version_requirement:
29
- version_requirements: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: 2.0.4
34
- version:
34
+ version_requirements: *id001
35
35
  - !ruby/object:Gem::Dependency
36
36
  name: hoe
37
- type: :development
38
- version_requirement:
39
- version_requirements: !ruby/object:Gem::Requirement
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
40
  requirements:
41
41
  - - ">="
42
42
  - !ruby/object:Gem::Version
43
- version: 2.6.0
44
- version:
43
+ hash: 41
44
+ segments:
45
+ - 2
46
+ - 9
47
+ - 1
48
+ version: 2.9.1
49
+ type: :development
50
+ version_requirements: *id002
45
51
  description: Value class for SMPTE timecode information
46
52
  email:
47
53
  - me@julik.nl
@@ -52,16 +58,18 @@ extensions: []
52
58
  extra_rdoc_files:
53
59
  - History.txt
54
60
  - Manifest.txt
55
- - README.txt
56
- - SPECS.txt
61
+ - README.rdoc
62
+ - SPECS.rdoc
57
63
  files:
64
+ - .DS_Store
58
65
  - History.txt
59
66
  - Manifest.txt
60
- - README.txt
61
- - SPECS.txt
67
+ - README.rdoc
62
68
  - Rakefile
69
+ - SPECS.rdoc
63
70
  - lib/timecode.rb
64
71
  - test/test_timecode.rb
72
+ - .gemtest
65
73
  has_rdoc: true
66
74
  homepage: http://guerilla-di.org/timecode
67
75
  licenses: []
@@ -69,25 +77,31 @@ licenses: []
69
77
  post_install_message:
70
78
  rdoc_options:
71
79
  - --main
72
- - README.txt
80
+ - README.rdoc
73
81
  require_paths:
74
82
  - lib
75
83
  required_ruby_version: !ruby/object:Gem::Requirement
84
+ none: false
76
85
  requirements:
77
86
  - - ">="
78
87
  - !ruby/object:Gem::Version
88
+ hash: 3
89
+ segments:
90
+ - 0
79
91
  version: "0"
80
- version:
81
92
  required_rubygems_version: !ruby/object:Gem::Requirement
93
+ none: false
82
94
  requirements:
83
95
  - - ">="
84
96
  - !ruby/object:Gem::Version
97
+ hash: 3
98
+ segments:
99
+ - 0
85
100
  version: "0"
86
- version:
87
101
  requirements: []
88
102
 
89
103
  rubyforge_project: guerilla-di
90
- rubygems_version: 1.3.5
104
+ rubygems_version: 1.4.1
91
105
  signing_key:
92
106
  specification_version: 3
93
107
  summary: Value class for SMPTE timecode information