mp3file 1.0.3 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fe6010e6463c0bfe01c34dcc53081a9cd3ff0dd7
4
- data.tar.gz: 88bcc7293fd8702980fb2344ef8d6e50457e7441
3
+ metadata.gz: 0bfcaa237ab4362c6e5ff6267f84c6becd95aab3
4
+ data.tar.gz: 3d86a90044fbf1ed37cf558a660e464993f71ec8
5
5
  SHA512:
6
- metadata.gz: b37617a0dabcc81bd28b4b1ee5c09bd472270792737fdc5298d20970063236a4afc635344a69730665085d518c0d8d42cfaefb211eb1dc73a20a5395301e1447
7
- data.tar.gz: 76330c2fc6d7654a634732f88652d46309dbefd5a92ad354e7dcc9dc9595e4017d6f014314852d754fbcf28b321577184e173205f90ec7e77fbac1f94aec443d
6
+ metadata.gz: 13e2cf72a0f6db63c9676d3a78eeed4345bd01e9cfb7487d1b24a864cf9ff35c9a4c945bf3890c97c269efb13ef0125177ee89902d2b5f8eb94f5c798faf36c2
7
+ data.tar.gz: e483622cdc9d209500c82809fb619f19e8ef0caa454c3ff209923d66438568b173c0d39cbe67c2852d16650f2b690b7b23b0c5b61a7e1d81c95bd4190fe1b16a
data/.gitignore CHANGED
@@ -2,3 +2,4 @@ pkg/*
2
2
  *.gem
3
3
  .bundle
4
4
  .DS_Store
5
+ /Gemfile.lock
@@ -3,21 +3,33 @@
3
3
  $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
4
4
  require 'mp3file'
5
5
 
6
+ def humanize(num, places = 2)
7
+ if num.is_a?(Float)
8
+ parts = ("%.#{places}f" % [ num ]).split(".")
9
+ else
10
+ parts = num.to_s.to_str.split(".")
11
+ end
12
+
13
+ parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1,")
14
+ parts.join(".")
15
+ end
16
+ alias :h :humanize
17
+
6
18
  ARGV.each do |file|
7
19
  begin
8
- mp3 = Mp3file::MP3File.new(file)
20
+ mp3 = Mp3file::MP3File.new(file, scan_all_headers: true)
9
21
 
10
22
  puts("File: #{mp3.file.path}")
11
- puts(" Size: #{mp3.file_size} bytes Audio_size: #{mp3.audio_size} bytes")
23
+ puts(" Size: #{h(mp3.file_size)} bytes Audio_size: #{h(mp3.audio_size)} bytes")
12
24
  puts(" #{mp3.mpeg_version} #{mp3.layer}")
13
- puts(" Bitrate: #{mp3.bitrate} kbps Samplerate: #{mp3.samplerate} Hz Mode: #{mp3.mode}")
14
- puts(" Duration: #{mp3.length} s")
15
- puts(" Frames: #{mp3.num_frames} Samples: #{mp3.total_samples}")
25
+ puts(" Bitrate: #{h(mp3.bitrate)} kbits/sec (vbr? #{mp3.vbr?.to_s}) Samplerate: #{mp3.samplerate} Hz Mode: #{mp3.mode}")
26
+ puts(" Duration: #{h(mp3.length)} s")
27
+ puts(" Frames: #{h(mp3.num_frames)} Samples: #{h(mp3.total_samples)}")
16
28
 
17
29
  header = mp3.first_header
18
- puts(" First MPEG frame at #{mp3.first_header_offset} bytes:")
30
+ puts(" First MPEG frame at #{h(mp3.first_header_offset)} bytes:")
19
31
  puts(" #{header.version} #{header.layer}")
20
- puts(" Bitrate: #{header.bitrate} bps Samplerate: #{header.samplerate} Hz")
32
+ puts(" Bitrate: #{header.bitrate} bps Samplerate: #{h(header.samplerate)} Hz")
21
33
  puts(" Mode: #{header.mode} Mode Extension: #{header.mode_extension} Emphasis: #{header.emphasis}")
22
34
  puts(" CRC: #{header.has_crc} Padding: #{header.has_padding}")
23
35
  puts(" Copyright: #{header.copyright} Original: #{header.original}")
@@ -26,7 +38,7 @@ ARGV.each do |file|
26
38
  if mp3.xing_header
27
39
  xing = mp3.xing_header
28
40
  puts(" Xing header:")
29
- puts(" Frames: #{xing.frames} Bytes: #{xing.bytes} Quality: #{xing.quality} TOC: #{xing.toc ? 'present' : 'not present'}")
41
+ puts(" Frames: #{h(xing.frames)} Bytes: #{h(xing.bytes)} Quality: #{xing.quality} TOC: #{xing.toc ? 'present' : 'not present'}")
30
42
  end
31
43
 
32
44
  if mp3.vbri_header
@@ -47,13 +59,13 @@ ARGV.each do |file|
47
59
  if mp3.id3v2tag?
48
60
  tag = mp3.id3v2_tag
49
61
  puts(" ID3v2 tag:")
50
- puts(" Size: #{tag.size} bytes")
62
+ puts(" Size: #{h(tag.size)} bytes")
51
63
  puts(" Version: #{tag.version.inspect}")
52
- puts(" Unused byes: #{tag.unused_bytes.inspect}")
64
+ puts(" Unused byes: #{h(tag.unused_bytes)}")
53
65
  if !tag.frames.empty?
54
66
  puts(" Frames:")
55
67
  tag.frames.each do |frame|
56
- puts(" ID: #{frame.frame_id.inspect} size: #{frame.frame_size}")
68
+ puts(" ID: #{frame.frame_id.inspect} size: #{h(frame.frame_size)}")
57
69
  end
58
70
  end
59
71
  end
@@ -63,11 +75,11 @@ ARGV.each do |file|
63
75
  puts(" Offset: #{offset} bytes")
64
76
  puts(" Size: #{tag2.size} bytes")
65
77
  puts(" Version: #{tag2.version.inspect}")
66
- puts(" Unused byes: #{tag2.unused_bytes.inspect}")
78
+ puts(" Unused byes: #{h(tag2.unused_bytes)}")
67
79
  if !tag2.frames.empty?
68
80
  puts(" Frames:")
69
81
  tag2.frames.each do |frame|
70
- puts(" ID: #{frame.frame_id.inspect} size: #{frame.frame_size}")
82
+ puts(" ID: #{frame.frame_id.inspect} size: #{h(frame.frame_size)}")
71
83
  end
72
84
  end
73
85
  end
@@ -61,6 +61,7 @@ module Mp3file::ID3v2
61
61
  if @tag.version >= ID3V2_2_0 && @tag.version < ID3V2_3_0
62
62
  header = ID3v220FrameHeaderFormat.read(io)
63
63
  @header_size = 6
64
+ @frame_size = header.frame_size
64
65
  elsif @tag.version >= ID3V2_3_0 && @tag.version < ID3V2_4_0
65
66
  header = ID3v230FrameHeaderFormat.read(io)
66
67
  @header_size = 10
@@ -75,16 +76,17 @@ module Mp3file::ID3v2
75
76
  if header.has_group == 1
76
77
  @group = header.group_id
77
78
  end
79
+ @frame_size = header.frame_size
78
80
  elsif @tag.version >= ID3V2_4_0
79
81
  header = ID3v240FrameHeaderFormat.read(io)
80
82
  @header_size = 10
83
+ @frame_size = BitPaddedInt.unpad_number(header.frame_size)
81
84
  end
82
85
  rescue BinData::ValidityError => ve
83
86
  raise InvalidID3v2TagError, ve.message
84
87
  end
85
88
 
86
89
  @frame_id = header.frame_id
87
- @frame_size = BitPaddedInt.unpad_number(header.frame_size)
88
90
  @size = @header_size + @frame_size
89
91
  end
90
92
  end
@@ -25,11 +25,14 @@ module Mp3file::ID3v2
25
25
  data.force_encoding("ASCII-8BIT")
26
26
  offset = 0
27
27
 
28
- frame_offset, frame = get_next_frame_header(data, offset)
29
- while frame
30
- @frames << frame
31
- offset = frame_offset + frame.size
28
+ loop do
32
29
  frame_offset, frame = get_next_frame_header(data, offset)
30
+ if frame
31
+ @frames << frame
32
+ offset = frame_offset + frame.size
33
+ else
34
+ break
35
+ end
33
36
  end
34
37
  end
35
38
 
@@ -11,9 +11,9 @@ module Mp3file
11
11
 
12
12
  attr_accessor(:id3v1_tag, :id3v2_tag, :extra_id3v2_tags)
13
13
 
14
- def initialize(file_path)
14
+ def initialize(file_path, options = {})
15
15
  file_path = Pathname.new(file_path).expand_path if file_path.is_a?(String)
16
- load_file(file_path)
16
+ load_file(file_path, options)
17
17
  end
18
18
 
19
19
  def vbr?
@@ -82,7 +82,9 @@ module Mp3file
82
82
  end
83
83
  end
84
84
 
85
- def load_file(file_path)
85
+ def load_file(file_path, options = {})
86
+ scan_all_headers = %w{ t true 1 }.include?(options[:scan_all_headers].to_s)
87
+
86
88
  @file = file_path.open('rb')
87
89
  @file.seek(0, IO::SEEK_END)
88
90
  @file_size = @file.tell
@@ -150,8 +152,8 @@ module Mp3file
150
152
  if @id3v1_tag
151
153
  @audio_size -= 128
152
154
  end
153
- if @id3v2_header
154
- @audio_size -= (@id3v2_header.tag_size + 10)
155
+ if @id3v2_tag
156
+ @audio_size -= @id3v2_tag.size
155
157
  end
156
158
 
157
159
  # If it's VBR, there should be an Xing header after the
@@ -164,22 +166,43 @@ module Mp3file
164
166
  @file.seek(@first_header_offset + 4, IO::SEEK_CUR)
165
167
  end
166
168
 
167
- if @xing_header
168
- @vbr = true
169
- # Do the VBR length calculation. What to do if we don't have
170
- # both of these pieces of information?
171
- if @xing_header.frames && @xing_header.bytes
172
- @num_frames = @xing_header.frames
173
- @total_samples = @xing_header.frames * @first_header.samples
174
- @length = total_samples.to_f / @samplerate.to_f
175
- @bitrate = ((@xing_header.bytes.to_f / @length.to_f) * 8 / 1000).to_i
169
+ if scan_all_headers
170
+ # Loop through all the frame headers, to check for VBR / CBR (as
171
+ # a Xing header can show up in either case).
172
+ frame_headers = [ @first_header ]
173
+ last_header_offset = @first_header_offset
174
+ loop do
175
+ file.seek(last_header_offset + frame_headers.last.frame_size)
176
+ last_header_offset, header = get_next_header(file)
177
+ if header.nil?
178
+ break
179
+ else
180
+ frame_headers << header
181
+ end
182
+ end
183
+
184
+ uniq_brs = frame_headers.map { |h| h.bitrate }.uniq
185
+ @vbr = uniq_brs.size > 1
186
+ if uniq_brs.size == 1
187
+ @bitrate = uniq_brs.first / 1000
176
188
  end
189
+ else
190
+ # Use the Xing header to make the VBR / CBR call. Assume that
191
+ # Xing headers, when present in a CBR file, are called "Info".
192
+ @vbr = @xing_header.nil? || @xing_header.name == "Xing"
193
+ end
194
+
195
+ if @xing_header && @xing_header.frames && @xing_header.bytes
196
+ # Use the Xing header to calculate the duration (and overall bitrate).
197
+ @num_frames = @xing_header.frames
198
+ @total_samples = @xing_header.frames * @first_header.samples
199
+ @length = total_samples.to_f / @samplerate.to_f
200
+ @bitrate = ((@xing_header.bytes.to_f / @length.to_f) * 8 / 1000)
177
201
  else
178
202
  # Do the CBR length calculation.
179
- @vbr = false
180
203
  @num_frames = @audio_size / @first_header.frame_size
181
204
  @total_samples = @num_frames * @first_header.samples
182
- @length = @total_samples / @samplerate
205
+ @length = @total_samples.to_f / @samplerate.to_f
183
206
  end
184
207
 
185
208
  @file.close
@@ -206,6 +229,8 @@ module Mp3file
206
229
  file.seek(header_offset, IO::SEEK_SET)
207
230
  retry
208
231
  end
232
+ rescue EOFError
233
+ break
209
234
  end
210
235
 
211
236
  # byte = file.readbyte
@@ -226,6 +251,10 @@ module Mp3file
226
251
  # end
227
252
  end
228
253
 
254
+ # if initial_header_offset != header_offset
255
+ # puts "Had to skip past #{header_offset - initial_header_offset} to find the next header."
256
+ # end
257
+
229
258
  [ header_offset, header ]
230
259
  end
231
260
  end
@@ -1,3 +1,3 @@
1
1
  module Mp3file
2
- VERSION = "1.0.3"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -2,7 +2,7 @@ module Mp3file
2
2
  class InvalidXingHeaderError < Mp3fileError; end
3
3
 
4
4
  class XingHeader
5
- attr_reader(:frames, :bytes, :toc, :quality)
5
+ attr_reader(:name, :frames, :bytes, :toc, :quality)
6
6
 
7
7
  class XingHeaderFormat < BinData::Record
8
8
  string(:vbr_id, :length => 4, :check_value => lambda { value == 'Xing' || value == 'Info' })
@@ -30,6 +30,7 @@ module Mp3file
30
30
  raise InvalidXingHeaderError, ve.message
31
31
  end
32
32
 
33
+ @name = head.vbr_id
33
34
  @frames = head.frames if head.frames_present == 1
34
35
  @bytes = head.bytes if head.bytes_present == 1
35
36
  @toc = head.toc.dup if head.toc_present == 1
@@ -17,6 +17,8 @@ Gem::Specification.new do |s|
17
17
 
18
18
  s.add_development_dependency('rspec')
19
19
  s.add_development_dependency('rake')
20
+ s.add_development_dependency('pry')
21
+
20
22
  s.add_dependency('bindata', '~> 1.5.0')
21
23
 
22
24
  s.files = `git ls-files`.split("\n")
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mp3file
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Watts
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-01-28 00:00:00.000000000 Z
11
+ date: 2015-06-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: bindata
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -65,7 +79,6 @@ files:
65
79
  - ".ruby-gemset"
66
80
  - ".ruby-version"
67
81
  - Gemfile
68
- - Gemfile.lock
69
82
  - README.md
70
83
  - Rakefile
71
84
  - bin/mp3info
@@ -1,29 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- mp3file (1.0.3)
5
- bindata (~> 1.5.0)
6
-
7
- GEM
8
- remote: http://rubygems.org/
9
- specs:
10
- bindata (1.5.1)
11
- diff-lcs (1.1.2)
12
- rake (0.9.2)
13
- rspec (2.6.0)
14
- rspec-core (~> 2.6.0)
15
- rspec-expectations (~> 2.6.0)
16
- rspec-mocks (~> 2.6.0)
17
- rspec-core (2.6.4)
18
- rspec-expectations (2.6.0)
19
- diff-lcs (~> 1.1.2)
20
- rspec-mocks (2.6.0)
21
-
22
- PLATFORMS
23
- ruby
24
- x86-mingw32
25
-
26
- DEPENDENCIES
27
- mp3file!
28
- rake
29
- rspec