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 +4 -4
- data/.gitignore +1 -0
- data/bin/mp3info +25 -13
- data/lib/mp3file/id3v2/frame_header.rb +3 -1
- data/lib/mp3file/id3v2/tag.rb +7 -4
- data/lib/mp3file/mp3_file.rb +45 -16
- data/lib/mp3file/version.rb +1 -1
- data/lib/mp3file/xing_header.rb +2 -1
- data/mp3file.gemspec +2 -0
- metadata +16 -3
- data/Gemfile.lock +0 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0bfcaa237ab4362c6e5ff6267f84c6becd95aab3
|
4
|
+
data.tar.gz: 3d86a90044fbf1ed37cf558a660e464993f71ec8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 13e2cf72a0f6db63c9676d3a78eeed4345bd01e9cfb7487d1b24a864cf9ff35c9a4c945bf3890c97c269efb13ef0125177ee89902d2b5f8eb94f5c798faf36c2
|
7
|
+
data.tar.gz: e483622cdc9d209500c82809fb619f19e8ef0caa454c3ff209923d66438568b173c0d39cbe67c2852d16650f2b690b7b23b0c5b61a7e1d81c95bd4190fe1b16a
|
data/.gitignore
CHANGED
data/bin/mp3info
CHANGED
@@ -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}
|
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
|
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
|
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
|
data/lib/mp3file/id3v2/tag.rb
CHANGED
@@ -25,11 +25,14 @@ module Mp3file::ID3v2
|
|
25
25
|
data.force_encoding("ASCII-8BIT")
|
26
26
|
offset = 0
|
27
27
|
|
28
|
-
|
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
|
|
data/lib/mp3file/mp3_file.rb
CHANGED
@@ -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 @
|
154
|
-
@audio_size -=
|
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
|
168
|
-
|
169
|
-
#
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
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
|
data/lib/mp3file/version.rb
CHANGED
data/lib/mp3file/xing_header.rb
CHANGED
@@ -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
|
data/mp3file.gemspec
CHANGED
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
|
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-
|
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
|
data/Gemfile.lock
DELETED
@@ -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
|