timecode 2.2.1 → 2.2.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 80fce38f051f8788789292d5583602e98283db90
4
- data.tar.gz: 8391c0b89202dc1a5b227eeb81e83dedb1fd0269
2
+ SHA256:
3
+ metadata.gz: 4b088cb1d23478711cf52e4162b3d0d5f491eb860f5766e974ab25213d13df84
4
+ data.tar.gz: 4282bda2694e2ef66121c9bf16dd0bd99725dab8164c56b83c783dad1ab547c4
5
5
  SHA512:
6
- metadata.gz: f27b6b9eac311f9439f9f3d1a83a47518a91092182f6946f23f571979e2423b913c23898bf280cc7710af5ffb751b959b7af85d17f0d9861a3ab78d1f5d25fc5
7
- data.tar.gz: e0f302f8835221704ed169731b638f3426d5aadce08f96ed2c696ef4b2c913a2da5b78a0b4d3fd21384b91af09816d5d03ccbaa5b8ce98bb4febaa679a1862a7
6
+ metadata.gz: bb70a54acff2a2915e86152537bc72a06f78af47f05103467c9016e98686dab75f9497f9778a400bdd8f7b5f1dc25f03607355bb21f75e0db69845cfe5c0e78d
7
+ data.tar.gz: b861e1b97cbd0bec353e809913700c476f833e60467cb4b5de4ed8c796de39ba3e41e380ca00cd8c7ffcd30c496d235867bd0ecf4c41072b5da70c80b02513f8
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ .DS_Store
2
+ **/.DS_Store
3
+ pkg
4
+ Gemfile.lock
5
+ .ruby-version
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ sudo: false
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.1.5
5
+ - jruby
6
+ - ruby-head
data/Gemfile CHANGED
@@ -1,15 +1,3 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gem 'approximately', '~> 1.1'
4
-
5
- group :development do
6
- if RUBY_VERSION < "1.9"
7
- gem "jeweler", '1.8.4' # Last one without the stupid nokogiri dependency
8
- else
9
- gem "jeweler"
10
- end
11
-
12
- gem "rake", '~> 10'
13
- gem 'git', '1.2.9.1'
14
- gem 'minitest'
15
- end
3
+ gemspec
data/History.txt CHANGED
@@ -1,3 +1,15 @@
1
+ === 2.2.3 / 2017-06-04
2
+
3
+ * Fix drop-frame timecode handling
4
+
5
+ === 2.2.2 / 2017-06-04
6
+
7
+ * Replace jeweler with standard rubygems tasks for rolling releases etc.
8
+
9
+ === 2.2.1 / 2016-06-06
10
+
11
+ * Add support for drop-frame timecode with proper calculations (pcipriano)
12
+
1
13
  === 2.1.0 / 2015-10-21
2
14
 
3
15
  * Allow hour counts larger than 99. Note that when printed using to_s the hours will roll over.
data/Rakefile CHANGED
@@ -1,27 +1,10 @@
1
- require 'rubygems'
2
- require 'jeweler'
3
- require './lib/timecode'
4
- require 'thread'
5
- Jeweler::Tasks.new do |gem|
6
- gem.version = Timecode::VERSION
7
- gem.name = "timecode"
8
- gem.summary = "Timecode value class"
9
- gem.email = "me@julik.nl"
10
- gem.homepage = "http://guerilla-di.org/timecode"
11
- gem.authors = ["Julik Tarkhanov"]
12
- gem.license = 'MIT'
13
-
14
- # Do not package invisibles
15
- gem.files.exclude ".*"
16
- end
17
-
1
+ require "bundler/gem_tasks"
18
2
  require 'rake/testtask'
3
+
19
4
  Rake::TestTask.new(:test) do |test|
20
5
  test.libs << 'lib' << 'test'
21
6
  test.pattern = 'test/**/test_*.rb'
22
7
  test.verbose = true
23
8
  end
24
9
 
25
- Jeweler::RubygemsDotOrgTasks.new
26
-
27
- task :default => [ :test ]
10
+ task :default => :test
@@ -0,0 +1,3 @@
1
+ class Timecode
2
+ VERSION = '2.2.3'
3
+ end
data/lib/timecode.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  # Timecode is a convenience object for calculating SMPTE timecode natively.
2
2
  # The promise is that you only have to store two values to know the timecode - the amount
3
- # of frames and the framerate. An additional perk might be to save the dropframeness,
4
- # but we avoid that at this point.
3
+ # of frames and the framerate.
5
4
  #
6
5
  # You can calculate in timecode objects as well as with conventional integers and floats.
7
6
  # Timecode is immutable and can be used as a value object. Timecode objects are sortable.
@@ -12,7 +11,7 @@
12
11
  # :mapping => [%w(source_tc_frames total), %w(tape_fps fps)]
13
12
 
14
13
  require "approximately"
15
-
14
+ require File.dirname(__FILE__) + '/timecode/version'
16
15
  class Timecode
17
16
 
18
17
  class ComputationValues
@@ -40,8 +39,6 @@ class Timecode
40
39
  end
41
40
  end
42
41
 
43
- VERSION = '2.2.1'
44
-
45
42
  include Comparable, Approximately
46
43
 
47
44
  DEFAULT_FPS = 25.0
@@ -212,7 +209,7 @@ class Timecode
212
209
  def at(hrs, mins, secs, frames, with_fps = DEFAULT_FPS, drop_frame = false)
213
210
  validate_atoms!(hrs, mins, secs, frames, with_fps)
214
211
  comp = ComputationValues.new(with_fps, drop_frame)
215
- if drop_frame && secs == 0 && (mins % 10) && (frames < comp.drop_count)
212
+ if drop_frame && secs == 0 && (mins % 10 > 0) && (frames < comp.drop_count)
216
213
  frames = comp.drop_count
217
214
  end
218
215
 
data/timecode.gemspec CHANGED
@@ -1,57 +1,34 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
- # -*- encoding: utf-8 -*-
5
- # stub: timecode 2.2.1 ruby lib
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'timecode/version'
6
5
 
7
- Gem::Specification.new do |s|
8
- s.name = "timecode"
9
- s.version = "2.2.1"
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "timecode"
8
+ spec.version = Timecode::VERSION
9
+ spec.authors = ["Julik Tarkhanov"]
10
+ spec.email = ["me@julik.nl"]
10
11
 
11
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
- s.require_paths = ["lib"]
13
- s.authors = ["Julik Tarkhanov"]
14
- s.date = "2016-06-06"
15
- s.email = "me@julik.nl"
16
- s.extra_rdoc_files = [
17
- "README.rdoc"
18
- ]
19
- s.files = [
20
- "Gemfile",
21
- "History.txt",
22
- "README.rdoc",
23
- "Rakefile",
24
- "lib/timecode.rb",
25
- "test/test_timecode.rb",
26
- "timecode.gemspec"
27
- ]
28
- s.homepage = "http://guerilla-di.org/timecode"
29
- s.licenses = ["MIT"]
30
- s.rubygems_version = "2.2.2"
31
- s.summary = "Timecode value class"
12
+ spec.homepage = "http://guerilla-di.org/timecode"
13
+ spec.summary = "Timecode value class"
14
+ spec.license = "MIT"
32
15
 
33
- if s.respond_to? :specification_version then
34
- s.specification_version = 4
35
-
36
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
37
- s.add_runtime_dependency(%q<approximately>, ["~> 1.1"])
38
- s.add_development_dependency(%q<jeweler>, [">= 0"])
39
- s.add_development_dependency(%q<rake>, ["~> 10"])
40
- s.add_development_dependency(%q<git>, ["= 1.2.9.1"])
41
- s.add_development_dependency(%q<minitest>, [">= 0"])
42
- else
43
- s.add_dependency(%q<approximately>, ["~> 1.1"])
44
- s.add_dependency(%q<jeweler>, [">= 0"])
45
- s.add_dependency(%q<rake>, ["~> 10"])
46
- s.add_dependency(%q<git>, ["= 1.2.9.1"])
47
- s.add_dependency(%q<minitest>, [">= 0"])
48
- end
16
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
17
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
18
+ if spec.respond_to?(:metadata)
19
+ spec.metadata['allowed_push_host'] = "https://rubygems.org"
49
20
  else
50
- s.add_dependency(%q<approximately>, ["~> 1.1"])
51
- s.add_dependency(%q<jeweler>, [">= 0"])
52
- s.add_dependency(%q<rake>, ["~> 10"])
53
- s.add_dependency(%q<git>, ["= 1.2.9.1"])
54
- s.add_dependency(%q<minitest>, [">= 0"])
21
+ raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
55
22
  end
56
- end
57
23
 
24
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
25
+ spec.bindir = "exe"
26
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
+ spec.require_paths = ["lib"]
28
+
29
+ spec.add_dependency "approximately", '~> 1.1'
30
+
31
+ spec.add_development_dependency "minitest", "~> 1"
32
+ spec.add_development_dependency "bundler"
33
+ spec.add_development_dependency "rake", "~> 10.0"
34
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: timecode
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.1
4
+ version: 2.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Julik Tarkhanov
8
- autorequire:
9
- bindir: bin
8
+ autorequire:
9
+ bindir: exe
10
10
  cert_chain: []
11
- date: 2016-06-06 00:00:00.000000000 Z
11
+ date: 2024-05-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: approximately
@@ -25,80 +25,69 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.1'
27
27
  - !ruby/object:Gem::Dependency
28
- name: jeweler
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
- - !ruby/object:Gem::Dependency
42
- name: rake
28
+ name: minitest
43
29
  requirement: !ruby/object:Gem::Requirement
44
30
  requirements:
45
31
  - - "~>"
46
32
  - !ruby/object:Gem::Version
47
- version: '10'
33
+ version: '1'
48
34
  type: :development
49
35
  prerelease: false
50
36
  version_requirements: !ruby/object:Gem::Requirement
51
37
  requirements:
52
38
  - - "~>"
53
39
  - !ruby/object:Gem::Version
54
- version: '10'
40
+ version: '1'
55
41
  - !ruby/object:Gem::Dependency
56
- name: git
42
+ name: bundler
57
43
  requirement: !ruby/object:Gem::Requirement
58
44
  requirements:
59
- - - '='
45
+ - - ">="
60
46
  - !ruby/object:Gem::Version
61
- version: 1.2.9.1
47
+ version: '0'
62
48
  type: :development
63
49
  prerelease: false
64
50
  version_requirements: !ruby/object:Gem::Requirement
65
51
  requirements:
66
- - - '='
52
+ - - ">="
67
53
  - !ruby/object:Gem::Version
68
- version: 1.2.9.1
54
+ version: '0'
69
55
  - !ruby/object:Gem::Dependency
70
- name: minitest
56
+ name: rake
71
57
  requirement: !ruby/object:Gem::Requirement
72
58
  requirements:
73
- - - ">="
59
+ - - "~>"
74
60
  - !ruby/object:Gem::Version
75
- version: '0'
61
+ version: '10.0'
76
62
  type: :development
77
63
  prerelease: false
78
64
  version_requirements: !ruby/object:Gem::Requirement
79
65
  requirements:
80
- - - ">="
66
+ - - "~>"
81
67
  - !ruby/object:Gem::Version
82
- version: '0'
83
- description:
84
- email: me@julik.nl
68
+ version: '10.0'
69
+ description:
70
+ email:
71
+ - me@julik.nl
85
72
  executables: []
86
73
  extensions: []
87
- extra_rdoc_files:
88
- - README.rdoc
74
+ extra_rdoc_files: []
89
75
  files:
76
+ - ".gitignore"
77
+ - ".travis.yml"
90
78
  - Gemfile
91
79
  - History.txt
92
80
  - README.rdoc
93
81
  - Rakefile
94
82
  - lib/timecode.rb
95
- - test/test_timecode.rb
83
+ - lib/timecode/version.rb
96
84
  - timecode.gemspec
97
85
  homepage: http://guerilla-di.org/timecode
98
86
  licenses:
99
87
  - MIT
100
- metadata: {}
101
- post_install_message:
88
+ metadata:
89
+ allowed_push_host: https://rubygems.org
90
+ post_install_message:
102
91
  rdoc_options: []
103
92
  require_paths:
104
93
  - lib
@@ -113,9 +102,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
113
102
  - !ruby/object:Gem::Version
114
103
  version: '0'
115
104
  requirements: []
116
- rubyforge_project:
117
- rubygems_version: 2.2.2
118
- signing_key:
105
+ rubygems_version: 3.1.6
106
+ signing_key:
119
107
  specification_version: 4
120
108
  summary: Timecode value class
121
109
  test_files: []
@@ -1,662 +0,0 @@
1
- require 'rubygems'
2
- require 'minitest/spec'
3
- require 'minitest/autorun'
4
-
5
- require File.expand_path(File.dirname(__FILE__)) + '/../lib/timecode'
6
-
7
- # Needed for a number of tests from the past
8
- Timecode.add_custom_framerate!(10)
9
- Timecode.add_custom_framerate!(12.5)
10
- Timecode.add_custom_framerate!(57)
11
- Timecode.add_custom_framerate!(45)
12
- Timecode.add_custom_framerate!(12)
13
-
14
-
15
- describe "Timecode.new should" do
16
-
17
- it "instantiate from int" do
18
- tc = Timecode.new(10)
19
- tc.must_be_kind_of Timecode
20
- tc.total.must_equal 10
21
- end
22
-
23
- it "always coerce FPS to float" do
24
- Timecode.new(10, 24).fps.must_be_kind_of(Float)
25
- Timecode.new(10, 25.0).fps.must_be_kind_of(Float)
26
- Timecode.new(10, 29.97).fps.must_be_kind_of(Float)
27
- Timecode.new(10, 59.94).fps.must_be_kind_of(Float)
28
- end
29
-
30
- it "create a zero TC with no arguments" do
31
- Timecode.new.must_equal Timecode.new(0)
32
- end
33
-
34
- it "accept full string SMPTE timecode as well" do
35
- Timecode.new("00:25:30:10", 25).must_equal Timecode.parse("00:25:30:10")
36
- end
37
-
38
- it 'calculates correctly (spot check with special values)' do
39
- lambda{ Timecode.new 496159, 23.976 }.must_be_silent
40
- lambda{ Timecode.new 548999, 23.976 }.must_be_silent
41
- lambda{ Timecode.new 9662, 29.97 }.must_be_silent
42
- end
43
-
44
- it 'calculates seconds correctly for rational fps' do
45
- Timecode.new(548999, 23.976).seconds.must_equal 14
46
- Timecode.new(9662, 29.97, true).seconds.must_equal 22
47
- Timecode.new(1078920, 29.97, true).seconds.must_equal 0
48
- end
49
-
50
- it 'calculates timecode correctly for rational fps' do
51
- atoms_ok = lambda { |tc, h, m, s, f|
52
- tc.hours.must_equal h
53
- tc.minutes.must_equal m
54
- tc.seconds.must_equal s
55
- tc.frames.must_equal f
56
- }
57
-
58
- atoms_ok.call(Timecode.new(9662, 29.97, true), 0, 5, 22, 12)
59
- atoms_ok.call(Timecode.new(467637, 29.97, true), 4, 20, 3, 15)
60
- atoms_ok.call(Timecode.new(1078920, 29.97, true), 10, 0, 0, 0)
61
- end
62
- end
63
-
64
- describe "Timecode.validate_atoms! should" do
65
-
66
- it "disallow more than 999 hrs" do
67
- lambda{ Timecode.validate_atoms!(999,0,0,0, 25) }.must_be_silent
68
- lambda{ Timecode.validate_atoms!(1000,0,0,0, 25) }.must_raise(Timecode::RangeError)
69
- end
70
-
71
- it "disallow more than 59 minutes" do
72
- lambda{ Timecode.validate_atoms!(1,60,0,0, 25) }.must_raise(Timecode::RangeError)
73
- end
74
-
75
- it "disallow more than 59 seconds" do
76
- lambda{ Timecode.validate_atoms!(1,0,60,0, 25) }.must_raise(Timecode::RangeError)
77
- end
78
-
79
- it "disallow more frames than what the framerate permits" do
80
- lambda{ Timecode.validate_atoms!(1,0,45,25, 25) }.must_raise(Timecode::RangeError)
81
- lambda{ Timecode.validate_atoms!(1,0,45,32, 30) }.must_raise(Timecode::RangeError)
82
- end
83
-
84
- it "pass validation with usable values" do
85
- lambda{ Timecode.validate_atoms!(20, 20, 10, 5, 25)}.must_be_silent
86
- end
87
- end
88
-
89
- describe "Timecode.at should" do
90
-
91
- it "disallow more than 999 hrs" do
92
- lambda{ Timecode.at(999,0,0,0) }.must_be_silent
93
- lambda{ Timecode.at(1000,0,0,0) }.must_raise(Timecode::RangeError)
94
- end
95
-
96
- it "disallow more than 59 minutes" do
97
- lambda{ Timecode.at(1,60,0,0) }.must_raise(Timecode::RangeError)
98
- end
99
-
100
- it "disallow more than 59 seconds" do
101
- lambda{ Timecode.at(1,0,60,0) }.must_raise(Timecode::RangeError)
102
- end
103
-
104
- it "disallow more frames than what the framerate permits" do
105
- lambda{ Timecode.at(1,0,60,25, 25) }.must_raise(Timecode::RangeError)
106
- lambda{ Timecode.at(1,0,60,32, 30) }.must_raise(Timecode::RangeError)
107
- end
108
-
109
- it "properly accept usable values" do
110
- Timecode.at(20, 20, 10, 5).to_s.must_equal "20:20:10:05"
111
- end
112
- end
113
-
114
- describe "A new Timecode object should" do
115
- it "be frozen" do
116
- # must_be :frozen? is somehow dead too
117
- assert Timecode.new(10).frozen?
118
- end
119
- end
120
-
121
- describe "An existing Timecode should" do
122
-
123
- before do
124
- @five_seconds = Timecode.new(5*25, 25)
125
- @one_and_a_half_film = (90 * 60) * 24
126
- @film_tc = Timecode.new(@one_and_a_half_film, 24)
127
- end
128
-
129
- it "report that the framerates are in delta" do
130
- tc = Timecode.new(1)
131
- tc.framerate_in_delta(25.0000000000000001, 25.0000000000000003).must_equal(true)
132
- end
133
-
134
- it "validate equality based on delta" do
135
- t1, t2 = Timecode.new(10, 25.0000000000000000000000000001), Timecode.new(10, 25.0000000000000000000000000002)
136
- t1.must_equal(t2)
137
- end
138
-
139
- it "report total as it's to_i" do
140
- Timecode.new(10).to_i.must_equal(10)
141
- end
142
-
143
- it "coerce itself to int" do
144
- (10 + Timecode.new(2)).must_equal 12
145
- end
146
-
147
- it "support hours" do
148
- @five_seconds.must_respond_to :hours
149
- @five_seconds.hours.must_equal 0
150
- @film_tc.hours.must_equal 1
151
- end
152
-
153
- it "support minutes" do
154
- @five_seconds.must_respond_to :minutes
155
- @five_seconds.minutes.must_equal 0
156
- @film_tc.minutes.must_equal 30
157
- end
158
-
159
- it "support seconds" do
160
- @five_seconds.must_respond_to :seconds
161
- @five_seconds.seconds.must_equal 5
162
- @film_tc.seconds.must_equal 0
163
- end
164
-
165
- it "support frames" do
166
- @film_tc.frames.must_equal 0
167
- end
168
-
169
- it "report frame_interval as a float" do
170
- tc = Timecode.new(10)
171
- tc.must_respond_to :frame_interval
172
-
173
- tc.frame_interval.must_be_within_delta 0.04, 0.0001
174
- tc = Timecode.new(10, 30)
175
- tc.frame_interval.must_be_within_delta 0.03333, 0.0001
176
- end
177
-
178
- it "be comparable" do
179
- (Timecode.new(10) < Timecode.new(9)).must_equal false
180
- (Timecode.new(9) < Timecode.new(10)).must_equal true
181
- Timecode.new(9).must_equal Timecode.new(9)
182
- end
183
-
184
- it "raise on comparison of incompatible timecodes" do
185
- lambda { Timecode.new(10, 10) < Timecode.new(10, 20)}.must_raise(Timecode::WrongFramerate)
186
- end
187
- end
188
-
189
- #module MiniTest::Assertions
190
- # UNDEFINED = MiniTest::Assertions::UNDEFINED
191
- # def assert_operator o1, op, o2 = UNDEFINED, msg = nil
192
- # puts [o1, op, o2, msg].inspect
193
- # return assert_predicate o1, op, msg if UNDEFINED == o2
194
- # msg = message(msg) { "Expected #{mu_pp(o1)} to be #{op} #{mu_pp(o2)}" }
195
- # assert o1.__send__(op, o2), msg
196
- # end
197
- #end
198
-
199
- describe "A Timecode of zero should" do
200
- it "properly respond to zero?" do
201
- Timecode.new(0).must_respond_to :zero?
202
- # must_be :zero? is somehow broken
203
- assert Timecode.new(0).zero?
204
- refute Timecode.new(1).zero?
205
- end
206
- end
207
-
208
- describe "Timecode.from_seconds should" do
209
- it "properly process this specific case for a float framerate" do
210
- float_secs = 89.99165971643036
211
- float_fps = 23.9898
212
- Timecode.add_custom_framerate!(float_fps)
213
- lambda{ Timecode.from_seconds(float_secs, float_fps) }.must_be_silent
214
- end
215
-
216
- it "properly process a DF framerate" do
217
- Timecode.from_seconds(322.4004, 29.97, true).to_i.must_equal 9662
218
- Timecode.from_seconds(600.0, 29.97, true).to_i.must_equal 17982
219
- Timecode.from_seconds(15603.5005, 29.97, true).to_i.must_equal 467637
220
- Timecode.from_seconds(36000.0, 29.97, true).to_i.must_equal 1078920
221
- end
222
- end
223
-
224
- describe "Timecode#to_seconds should" do
225
- it "return a float" do
226
- Timecode.new(0).to_seconds.must_be_kind_of Float
227
- end
228
-
229
- it "return the value in seconds" do
230
- fps = 24
231
- secs = 126.3
232
- Timecode.new(fps * secs, fps).to_seconds.must_be_within_delta 126.3, 0.1
233
- end
234
-
235
- it "properly roundtrip a value via Timecode.from_seconds" do
236
- secs_in = 19.76
237
- from_secs = Timecode.from_seconds(secs_in, 25.0)
238
- from_secs.total.must_equal 494
239
- from_secs.to_seconds.must_be_within_delta secs_in, 0.001
240
-
241
- secs_in = 15603.50
242
- from_secs = Timecode.from_seconds(secs_in, 29.97, true)
243
- from_secs.total.must_equal 467637
244
- from_secs.to_seconds.must_be_within_delta secs_in, 0.005
245
- end
246
- end
247
-
248
- describe "An existing Timecode on inspection should" do
249
- it "properly present himself via inspect" do
250
- Timecode.new(10, 25).inspect.must_equal "#<Timecode:00:00:00:10 (10F@25.00)>"
251
- Timecode.new(10, 12).inspect.must_equal "#<Timecode:00:00:00:10 (10F@12.00)>"
252
- end
253
-
254
- it "properly print itself" do
255
- Timecode.new(5, 25).to_s.must_equal "00:00:00:05"
256
- end
257
-
258
- it "properly print itself with DF" do
259
- Timecode.new(9662, 29.97, true).to_s.must_equal "00:05:22;12"
260
- Timecode.new(9662, 29.97, false).to_s.must_equal "00:05:22:02"
261
- end
262
- end
263
-
264
- describe "An existing Timecode compared by adjacency" do
265
- it "properly detect an adjacent timecode to the left" do
266
- Timecode.new(10).must_be :adjacent_to?, Timecode.new(9)
267
- Timecode.new(10).wont_be :adjacent_to?, Timecode.new(8)
268
- end
269
-
270
- it "properly detect an adjacent DF timecode to the left" do
271
- Timecode.new(1800, 29.97, true).must_be :adjacent_to?, Timecode.new(1799, 29.97, true)
272
- Timecode.new(1800, 29.97, true).wont_be :adjacent_to?, Timecode.new(1798, 29.97, true)
273
- end
274
-
275
- it "properly detect an adjacent timecode to the right" do
276
- Timecode.new(10).must_be :adjacent_to?, Timecode.new(11)
277
- Timecode.new(10).wont_be :adjacent_to?, Timecode.new(12)
278
- end
279
-
280
- it "properly detect an adjacent DF timecode to the right" do
281
- Timecode.new(1799, 29.97, true).must_be :adjacent_to?, Timecode.new(1800, 29.97, true)
282
- Timecode.new(1799, 29.97, true).wont_be :adjacent_to?, Timecode.new(1801, 29.97, true)
283
- end
284
- end
285
-
286
- describe "A Timecode on conversion should" do
287
- it "copy itself with a different framerate" do
288
- tc = Timecode.new(1800, 25)
289
- at24 = tc.convert(24)
290
- at24.total.must_equal 1800
291
- at29 = tc.convert(29.97)
292
- at29.total.must_equal 1800
293
- at29.to_s.must_equal "00:01:00:00"
294
- at29DF = tc.convert(29.97, true)
295
- at29DF.total.must_equal 1800
296
- at29DF.to_s.must_equal "00:01:00;02"
297
-
298
- tc1 = Timecode.new(1800, 23.976, true)
299
- at29 = tc1.convert(29.97)
300
- at29.total.must_equal 1800
301
- at29.to_s.must_equal "00:01:00;02"
302
- at29ND = tc1.convert(29.97, false)
303
- at29ND.total.must_equal 1800
304
- at29ND.to_s.must_equal "00:01:00:00"
305
- end
306
- end
307
-
308
- describe "An existing Timecode used within ranges should" do
309
- it "properly provide successive value that is one frame up" do
310
- Timecode.new(10).succ.total.must_equal 11
311
- Timecode.new(22, 45).succ.must_equal Timecode.new(23, 45)
312
- Timecode.new(1799, 29.97, true).succ.must_equal Timecode.new(1800, 29.97, true)
313
- end
314
-
315
- it "work as a range member" do
316
- r = Timecode.new(10)...Timecode.new(20)
317
- r.to_a.length.must_equal 10
318
- r.to_a[4].must_equal Timecode.new(14)
319
- end
320
-
321
- end
322
-
323
- describe "A Timecode on calculations should" do
324
-
325
- it "support addition" do
326
- a, b = Timecode.new(24, 25.000000000000001), Timecode.new(22, 25.000000000000002)
327
- (a + b).must_equal Timecode.new(24 + 22, 25.000000000000001)
328
- end
329
-
330
- it "should raise on addition if framerates do not match" do
331
- lambda{ Timecode.new(10, 25) + Timecode.new(10, 30) }.must_raise(Timecode::WrongFramerate)
332
- end
333
-
334
- it "should raise on addition if drop flag mismatches" do
335
- lambda{ Timecode.new(10, 29.97, true) + Timecode.new(10, 29.97) }.must_raise(Timecode::WrongDropFlag)
336
- end
337
-
338
- it "when added with an integer instead calculate on total" do
339
- (Timecode.new(5) + 5).must_equal(Timecode.new(10))
340
- end
341
-
342
- it "when adding DF flag is preserved" do
343
- a, b = Timecode.new(24, 29.97, true), Timecode.new(22, 29.97, true)
344
- c, d = Timecode.new(24, 29.97), Timecode.new(22, 29.97)
345
- tcsum = a + b
346
- tcsum.must_equal Timecode.new(24 + 22, 29.97, true)
347
- tcsum.drop?.must_equal true
348
- (c + d).drop?.must_equal false
349
- end
350
-
351
- it "support subtraction" do
352
- a, b = Timecode.new(10), Timecode.new(4)
353
- (a - b).must_equal Timecode.new(6)
354
- end
355
-
356
- it "on subtraction of an integer instead calculate on total" do
357
- (Timecode.new(15) - 5).must_equal Timecode.new(10)
358
- end
359
-
360
- it "raise when subtracting a Timecode with a different framerate" do
361
- lambda { Timecode.new(10, 25) - Timecode.new(10, 30) }.must_raise(Timecode::WrongFramerate)
362
- end
363
-
364
- it "raise when subtracting a Timecode with a different drop frame flag" do
365
- lambda { Timecode.new(10, 29.97, true) - Timecode.new(10, 29.97) }.must_raise(Timecode::WrongDropFlag)
366
- end
367
-
368
- it "when subtracting DF flag is preserved" do
369
- a, b = Timecode.new(24, 29.97, true), Timecode.new(22, 29.97, true)
370
- c, d = Timecode.new(24, 29.97), Timecode.new(22, 29.97)
371
- tcsub = a - b
372
- tcsub.must_equal Timecode.new(24 - 22, 29.97, true)
373
- tcsub.drop?.must_equal true
374
- (c - d).drop?.must_equal false
375
- end
376
-
377
- it "support multiplication" do
378
- (Timecode.new(10) * 10).must_equal(Timecode.new(100))
379
- end
380
-
381
- it "raise when the resultig Timecode is negative" do
382
- lambda { Timecode.new(10) * -200 }.must_raise(Timecode::RangeError)
383
- end
384
-
385
- it "preserves drop frame flag when multiplying" do
386
- (Timecode.new(10, 29.97, true) * 10).drop?.must_equal true
387
- end
388
-
389
- it "return a Timecode when divided by an Integer" do
390
- v = Timecode.new(200) / 20
391
- v.must_be_kind_of(Timecode)
392
- v.must_equal Timecode.new(10)
393
- end
394
-
395
- it "return a number when divided by another Timecode" do
396
- v = Timecode.new(200) / Timecode.new(20)
397
- v.must_be_kind_of(Numeric)
398
- v.must_equal 10
399
- end
400
-
401
- it "preserves drop frame flag when dividing" do
402
- (Timecode.new(200, 29.97, true) / 20).drop?.must_equal true
403
- end
404
- end
405
-
406
- describe "A Timecode used with fractional number of seconds" do
407
-
408
- it "should properly return fractional seconds" do
409
- tc = Timecode.new(100 - 1, fps = 25)
410
- tc.frames.must_equal 24
411
-
412
- tc.with_frames_as_fraction.must_equal "00:00:03.96"
413
- tc.with_fractional_seconds.must_equal "00:00:03.96"
414
- tc.with_srt_fraction.must_equal "00:00:03,96"
415
- end
416
-
417
- it "properly translate to frames when instantiated from fractional seconds" do
418
- fraction = 7.1
419
- tc = Timecode.from_seconds(fraction, 10)
420
- tc.to_s.must_equal "00:00:07:01"
421
-
422
- fraction = 7.5
423
- tc = Timecode.from_seconds(fraction, 10)
424
- tc.to_s.must_equal "00:00:07:05"
425
-
426
- fraction = 7.16
427
- tc = Timecode.from_seconds(fraction, 25)
428
- tc.to_s.must_equal "00:00:07:04"
429
- end
430
-
431
- end
432
-
433
- describe "A custom Timecode descendant should" do
434
- class CustomTC < Timecode; end
435
-
436
- it "properly classify on parse" do
437
- CustomTC.parse("001").must_be_kind_of CustomTC
438
- end
439
-
440
- it "properly classify on at" do
441
- CustomTC.at(10,10,10,10).must_be_kind_of CustomTC
442
- end
443
-
444
- it "properly classify on calculations" do
445
- computed = CustomTC.parse("10h") + Timecode.new(10)
446
- computed.must_be_kind_of CustomTC
447
-
448
- computed = CustomTC.parse("10h") - Timecode.new(10)
449
- computed.must_be_kind_of CustomTC
450
-
451
- computed = CustomTC.parse("10h") * 5
452
- computed.must_be_kind_of CustomTC
453
-
454
- computed = CustomTC.parse("10h") / 5
455
- computed.must_be_kind_of CustomTC
456
- end
457
-
458
- end
459
-
460
- describe "Timecode.from_filename_in_sequence should" do
461
- it "detect the timecode" do
462
- tc = Timecode.from_filename_in_sequence("foobar.0000012.jpg", fps = 25)
463
- tc.must_equal(Timecode.new(12, 25))
464
- end
465
- end
466
-
467
- describe 'Timecode with hours larger than 99 should' do
468
- it 'print itself without rollover' do
469
- tc = Timecode.at(129,34,42,5)
470
- tc.to_s_without_rollover.must_equal '129:34:42:05'
471
- end
472
-
473
- it 'print itself with rollover when using to_smpte' do
474
- tc = Timecode.at(129,34,42,5)
475
- tc.to_s.must_equal '29:34:42:05'
476
- end
477
- end
478
-
479
- describe "Timecode.parse should" do
480
-
481
- it "handle complete SMPTE timecode" do
482
- simple_tc = "00:10:34:10"
483
- Timecode.parse(simple_tc).to_s.must_equal(simple_tc)
484
- end
485
-
486
- it "handle complete SMPTE timecode with drop frame flag" do
487
- simple_tc = "00:10:34;10"
488
- tc = Timecode.parse(simple_tc, 29.97)
489
- tc.to_s.must_equal(simple_tc)
490
- tc.drop?.must_equal true
491
- end
492
-
493
- it "handle complete SMPTE timecode with plus for 24 frames per second" do
494
- simple_tc = "00:10:34+10"
495
- p = Timecode.parse(simple_tc)
496
- p.to_s.must_equal("00:10:34:10")
497
- p.fps.must_equal 24
498
- end
499
-
500
- it "handle timecode with fractional seconds" do
501
- tc = Timecode.parse("10:10:10.2", 25)
502
- tc.to_s.must_equal "10:10:10:05"
503
-
504
- tc = Timecode.parse("10:10:10,200", 25)
505
- tc.to_s.must_equal "10:10:10:05"
506
- end
507
-
508
- it "handle timecode with fractional seconds (euro style, SRT)" do
509
- tc = Timecode.parse("10:10:10,200", 25)
510
- tc.to_s.must_equal "10:10:10:05"
511
- end
512
-
513
- it "handle timecode with ticks" do
514
- tc = Timecode.parse("10:10:10:103", 25)
515
- tc.to_s.must_equal "10:10:10:10"
516
-
517
- tc = Timecode.parse("10:10:10:249", 25)
518
- tc.to_s.must_equal "10:10:10:24"
519
- end
520
-
521
- it "raise when there are more than 249 ticks" do
522
- lambda {
523
- tc = Timecode.parse("10:10:10:250", 25)
524
- }.must_raise(Timecode::RangeError)
525
- end
526
-
527
- it "handle timecode with fractional seconds with spaces at start and end" do
528
- tc = Timecode.parse(" 00:00:01.040 ")
529
- tc.to_s.must_equal "00:00:01:01"
530
- end
531
-
532
- # I am commenting this one out for now, these were present in some odd subtitle file.
533
- # What we probably need is a way for Timecode to "extract" timecodes from a chunk of text.
534
- # it "handle timecode with fractional seconds with weirdo UTF spaces at start and end" do
535
- # tc = Timecode.parse("00:00:01.040")
536
- # tc.to_s.must_equal "00:00:01:01"
537
- # end
538
-
539
- it "parse a row of numbers as parts of a timecode starting from the right" do
540
- Timecode.parse("10").must_equal Timecode.new(10)
541
- Timecode.parse("210").must_equal Timecode.new(60)
542
- Timecode.parse("10101010").to_s.must_equal "10:10:10:10"
543
- end
544
-
545
- it "parse a number with f suffix as frames" do
546
- Timecode.parse("60f").must_equal Timecode.new(60)
547
- end
548
-
549
- it "parse a number with s suffix as seconds" do
550
- Timecode.parse("2s", 25).must_equal Timecode.new(50, 25)
551
- Timecode.parse("2s", 30).must_equal Timecode.new(60, 30)
552
- end
553
-
554
- it "parse a number with m suffix as minutes" do
555
- Timecode.parse("3m").must_equal Timecode.new(25 * 60 * 3)
556
- end
557
-
558
- it "parse a number with h suffix as hours" do
559
- Timecode.parse("3h").must_equal Timecode.new(25 * 60 * 60 * 3)
560
- end
561
-
562
- it "parse different suffixes as a sum of elements" do
563
- Timecode.parse("1h 4f").to_s.must_equal '01:00:00:04'
564
- Timecode.parse("4f 1h").to_s.must_equal '01:00:00:04'
565
- Timecode.parse("29f 1h").to_s.must_equal '01:00:01:04'
566
- Timecode.parse("29f \n\n\n\n\n\ 1h").to_s.must_equal '01:00:01:04'
567
- end
568
-
569
- it "parse a number of digits as timecode" do
570
- Timecode.parse("00000001").to_s.must_equal "00:00:00:01"
571
- Timecode.parse("1").to_s.must_equal "00:00:00:01"
572
- Timecode.parse("10").to_s.must_equal "00:00:00:10"
573
- end
574
-
575
- it "truncate a large number to the parseable length" do
576
- Timecode.parse("1000000000000000001").to_s.must_equal "10:00:00:00"
577
- end
578
-
579
- it "left-pad a large number to give proper TC" do
580
- Timecode.parse("123456", 57).to_s.must_equal "00:12:34:56"
581
- end
582
-
583
- it "parse timecode with fractional second instead of frames" do
584
- fraction = "00:00:07.1"
585
- tc = Timecode.parse_with_fractional_seconds(fraction, 10)
586
- tc.to_s.must_equal "00:00:07:01"
587
-
588
- fraction = "00:00:07.5"
589
- tc = Timecode.parse_with_fractional_seconds(fraction, 10)
590
- tc.to_s.must_equal "00:00:07:05"
591
-
592
- fraction = "00:00:07.04"
593
- tc = Timecode.parse_with_fractional_seconds(fraction, 12.5)
594
- tc.to_s.must_equal "00:00:07:00"
595
-
596
- fraction = "00:00:07.16"
597
- tc = Timecode.parse_with_fractional_seconds(fraction, 12.5)
598
- tc.to_s.must_equal "00:00:07:02"
599
- end
600
-
601
- it "reports DF timecode" do
602
- df_tc = "00:00:00;01"
603
- Timecode.parse(df_tc, 29.97).drop?.must_equal true
604
- end
605
-
606
- it "raise on improper format" do
607
- lambda { Timecode.parse("Meaningless nonsense", 25) }.must_raise Timecode::CannotParse
608
- lambda { Timecode.parse("", 25) }.must_raise Timecode::CannotParse
609
- end
610
-
611
- it "raise on empty argument" do
612
- lambda { Timecode.parse(" \n\n ", 25) }.must_raise Timecode::CannotParse
613
- end
614
-
615
- it "properly handle 09 and 08 as part of complete TC pattern" do
616
- Timecode.parse( "09:08:09:08", 25).total.must_equal 822233
617
- end
618
- end
619
-
620
- describe "Timecode.soft_parse should" do
621
- it "parse the timecode" do
622
- Timecode.soft_parse('200').to_s.must_equal "00:00:02:00"
623
- end
624
-
625
- it "not raise on improper format and return zero TC instead" do
626
- lambda do
627
- tc = Timecode.soft_parse("Meaningless nonsense", 25)
628
- tc.must_equal Timecode.new(0)
629
- end.must_be_silent
630
- end
631
- end
632
-
633
- describe 'Timecode#to_s' do
634
- it 'formats 25 and 25 FPS timecodes uniformly' do
635
- at25 = Timecode.parse("1h", 25)
636
- at24 = Timecode.parse("1h", 24)
637
- at25.to_s.must_equal "01:00:00:00"
638
- at24.to_s.must_equal "01:00:00:00"
639
- end
640
- end
641
-
642
- describe 'Timecode#inspect' do
643
- it 'formats 25 and 25 FPS timecodes differently' do
644
- at25 = Timecode.parse("1h", 25)
645
- at24 = Timecode.parse("1h", 24)
646
- at25.inspect.must_equal "#<Timecode:01:00:00:00 (90000F@25.00)>"
647
- at24.inspect.must_equal "#<Timecode:01:00:00+00 (86400F@24.00)>"
648
- end
649
- end
650
-
651
- describe "Timecode with unsigned integer conversions should" do
652
-
653
- it "parse from a 4x4bits packed 32bit unsigned int" do
654
- uint, tc = 87310853, Timecode.at(5,34,42,5)
655
- Timecode.from_uint(uint).must_equal tc
656
- end
657
-
658
- it "properly convert itself back to 4x4 bits 32bit unsigned int" do
659
- uint, tc = 87310853, Timecode.at(5,34,42,5)
660
- tc.to_uint.must_equal uint
661
- end
662
- end