timecode 0.2.1 → 1.1.0

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/.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