mp3file 0.0.4 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitattributes +22 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile.lock +1 -1
- data/README.md +14 -0
- data/bin/mp3info +80 -0
- data/lib/mp3file/id3v1_tag.rb +24 -10
- data/lib/mp3file/id3v2/frame_header.rb +15 -8
- data/lib/mp3file/id3v2/tag.rb +37 -3
- data/lib/mp3file/mp3_file.rb +42 -15
- data/lib/mp3file/version.rb +1 -1
- data/spec/mp3file/id3v1_tag_spec.rb +4 -4
- data/spec/mp3file/id3v2/frame_header_spec.rb +14 -14
- data/spec/mp3file/id3v2/tag_spec.rb +1 -5
- data/spec/mp3file/mp3_file_spec.rb +1 -1
- metadata +52 -27
- data/.rvmrc +0 -1
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 239aaea4897a1eb4494c5a1668ef5a8ce3e40645
|
4
|
+
data.tar.gz: eeeadc21db81420c767b4dd89283d30a3fa17436
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f1ff36c98e1ad5c084664cad9cc85d5b3d9c31bfd18d559295a259876410d1f55bdc656a4bc0cf7065c5ce94fbd6db88621674b33127daed3158eeda557cf512
|
7
|
+
data.tar.gz: 1abdf307e37627eecb33a813c1f205ffcec20ea8678c3943dd1f5f80354baf96c47f2949bfa29f3ab850c740230ee29b0724a22cd12d95f505663123a3648304
|
data/.gitattributes
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# Auto detect text files and perform LF normalization
|
2
|
+
* text=auto
|
3
|
+
|
4
|
+
# Custom for Visual Studio
|
5
|
+
*.cs diff=csharp
|
6
|
+
*.sln merge=union
|
7
|
+
*.csproj merge=union
|
8
|
+
*.vbproj merge=union
|
9
|
+
*.fsproj merge=union
|
10
|
+
*.dbproj merge=union
|
11
|
+
|
12
|
+
# Standard to msysgit
|
13
|
+
*.doc diff=astextplain
|
14
|
+
*.DOC diff=astextplain
|
15
|
+
*.docx diff=astextplain
|
16
|
+
*.DOCX diff=astextplain
|
17
|
+
*.dot diff=astextplain
|
18
|
+
*.DOT diff=astextplain
|
19
|
+
*.pdf diff=astextplain
|
20
|
+
*.PDF diff=astextplain
|
21
|
+
*.rtf diff=astextplain
|
22
|
+
*.RTF diff=astextplain
|
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
mp3file
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-2.1
|
data/Gemfile.lock
CHANGED
data/README.md
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# Mp3file
|
2
|
+
|
3
|
+
This is a pure-Ruby MP3 metadata extractor. We use it to identify the
|
4
|
+
bitrate and duration of songs that people upload to [http://www.reverbnation.com].
|
5
|
+
|
6
|
+
It handles a bunch of things:
|
7
|
+
* ID3v1 tags
|
8
|
+
* ID3v2 tags (although it doesn't actually parse the frames)
|
9
|
+
* VBR files (with Xing headers)
|
10
|
+
* Multiple ID3v2 tags (because these exist in the wild)
|
11
|
+
|
12
|
+
Don't think of this as an example of good code. This is some of the
|
13
|
+
most terrible, ugly, brutally hacky code I think I've ever written. I
|
14
|
+
apologize to anyone who looks at it.
|
data/bin/mp3info
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
|
4
|
+
require 'mp3file'
|
5
|
+
|
6
|
+
ARGV.each do |file|
|
7
|
+
begin
|
8
|
+
mp3 = Mp3file::MP3File.new(file)
|
9
|
+
|
10
|
+
puts("File: #{mp3.file.path}")
|
11
|
+
puts(" Size: #{mp3.file_size} bytes Audio_size: #{mp3.audio_size} bytes")
|
12
|
+
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}")
|
16
|
+
|
17
|
+
header = mp3.first_header
|
18
|
+
puts(" First MPEG frame at #{mp3.first_header_offset} bytes:")
|
19
|
+
puts(" #{header.version} #{header.layer}")
|
20
|
+
puts(" Bitrate: #{header.bitrate} bps Samplerate: #{header.samplerate} Hz")
|
21
|
+
puts(" Mode: #{header.mode} Mode Extension: #{header.mode_extension} Emphasis: #{header.emphasis}")
|
22
|
+
puts(" CRC: #{header.has_crc} Padding: #{header.has_padding}")
|
23
|
+
puts(" Copyright: #{header.copyright} Original: #{header.original}")
|
24
|
+
puts(" Samples: #{header.samples} Frame size: #{header.frame_size} Side bytes: #{header.side_bytes}")
|
25
|
+
|
26
|
+
if mp3.xing_header
|
27
|
+
xing = mp3.xing_header
|
28
|
+
puts(" Xing header:")
|
29
|
+
puts(" Frames: #{xing.frames} Bytes: #{xing.bytes} Quality: #{xing.quality} TOC: #{xing.toc ? 'present' : 'not present'}")
|
30
|
+
end
|
31
|
+
|
32
|
+
if mp3.vbri_header
|
33
|
+
puts(" VBRI header!")
|
34
|
+
end
|
35
|
+
|
36
|
+
if mp3.id3v1tag?
|
37
|
+
tag = mp3.id3v1_tag
|
38
|
+
puts(" ID3v1 tag:")
|
39
|
+
puts(" Title: #{tag.title.inspect}")
|
40
|
+
puts(" Artist: #{tag.artist.inspect}")
|
41
|
+
puts(" Album: #{tag.album.inspect}")
|
42
|
+
puts(" Year: #{tag.year.inspect}")
|
43
|
+
puts(" Comment: #{tag.comment.inspect}")
|
44
|
+
puts(" Genre: #{tag.genre.inspect} (#{tag.genre_id.inspect})")
|
45
|
+
end
|
46
|
+
|
47
|
+
if mp3.id3v2tag?
|
48
|
+
tag = mp3.id3v2_tag
|
49
|
+
puts(" ID3v2 tag:")
|
50
|
+
puts(" Size: #{tag.size} bytes")
|
51
|
+
puts(" Version: #{tag.version.inspect}")
|
52
|
+
puts(" Unused byes: #{tag.unused_bytes.inspect}")
|
53
|
+
if !tag.frames.empty?
|
54
|
+
puts(" Frames:")
|
55
|
+
tag.frames.each do |frame|
|
56
|
+
puts(" ID: #{frame.frame_id.inspect} size: #{frame.frame_size}")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
mp3.extra_id3v2_tags.each do |offset, tag2|
|
62
|
+
puts(" Extra ID3v2 tag:")
|
63
|
+
puts(" Offset: #{offset} bytes")
|
64
|
+
puts(" Size: #{tag2.size} bytes")
|
65
|
+
puts(" Version: #{tag2.version.inspect}")
|
66
|
+
puts(" Unused byes: #{tag2.unused_bytes.inspect}")
|
67
|
+
if !tag2.frames.empty?
|
68
|
+
puts(" Frames:")
|
69
|
+
tag2.frames.each do |frame|
|
70
|
+
puts(" ID: #{frame.frame_id.inspect} size: #{frame.frame_size}")
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
puts
|
76
|
+
rescue
|
77
|
+
$stderr.puts("Exception reading MP3 file %s: %s (%p)\n\t%s" %
|
78
|
+
[ file, $!.message, $!.class, $!.backtrace.join("\n\t") ])
|
79
|
+
end
|
80
|
+
end
|
data/lib/mp3file/id3v1_tag.rb
CHANGED
@@ -2,7 +2,7 @@ module Mp3file
|
|
2
2
|
class InvalidID3v1TagError < Mp3fileError; end
|
3
3
|
|
4
4
|
class ID3v1Tag
|
5
|
-
attr_accessor(:title, :artist, :album, :year, :comment, :track, :genre)
|
5
|
+
attr_accessor(:title, :artist, :album, :year, :comment, :track, :genre_id, :genre)
|
6
6
|
|
7
7
|
class ID3v1TagFormat < BinData::Record
|
8
8
|
string(:tag_id, :length => 3, :check_value => lambda { value == 'TAG' })
|
@@ -35,24 +35,38 @@ module Mp3file
|
|
35
35
|
Samba Folklore Ballad Power\ Ballad Rhythmic\ Soul Freestyle Duet Punk\ Rock
|
36
36
|
Drum\ Solo A\ capella Euro-House Dance\ Hall }
|
37
37
|
|
38
|
-
def
|
39
|
-
|
38
|
+
def self.parse(io)
|
39
|
+
tag_data = nil
|
40
|
+
|
40
41
|
begin
|
41
|
-
|
42
|
+
tag_data = ID3v1TagFormat.read(io)
|
42
43
|
rescue BinData::ValidityError => ve
|
43
44
|
raise InvalidID3v1TagError, ve.message
|
44
45
|
end
|
45
46
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
47
|
+
if tag_data.nil?
|
48
|
+
nil
|
49
|
+
else
|
50
|
+
new.tap { |rv| rv.load_format(tag_data) }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def load_format(tag_data)
|
55
|
+
@title = tag_data.title.split("\x00").first
|
56
|
+
@artist = tag_data.artist.split("\x00").first
|
57
|
+
@album = tag_data.album.split("\x00").first
|
58
|
+
@year = tag_data.year
|
59
|
+
split_comment = tag_data.comment.split("\x00").reject { |s| s == '' }
|
51
60
|
@comment = split_comment.first
|
52
61
|
if split_comment.size > 1
|
53
62
|
@track = split_comment.last.bytes.first
|
54
63
|
end
|
55
|
-
@
|
64
|
+
@genre_id = tag_data.genre_id
|
65
|
+
@genre = GENRES[tag_data.genre_id]
|
66
|
+
end
|
67
|
+
|
68
|
+
def initialize
|
69
|
+
@title = @artist = @album = @year = @comment = @track = @genre_id = @genre = nil
|
56
70
|
end
|
57
71
|
end
|
58
72
|
end
|
@@ -11,11 +11,11 @@ module Mp3file::ID3v2
|
|
11
11
|
bit1(:tag_alter_preserve)
|
12
12
|
bit1(:file_alter_preserve)
|
13
13
|
bit1(:read_only)
|
14
|
-
bit5(:unused1, :check_value => lambda { value == 0 })
|
14
|
+
bit5(:unused1) # , :check_value => lambda { value == 0 })
|
15
15
|
bit1(:compression)
|
16
16
|
bit1(:encryption)
|
17
17
|
bit1(:has_group)
|
18
|
-
bit5(:unused2, :check_value => lambda { value == 0 })
|
18
|
+
bit5(:unused2) # , :check_value => lambda { value == 0 })
|
19
19
|
uint8(:encryption_type, :onlyif => lambda { encryption == 1 })
|
20
20
|
uint8(:group_id, :onlyif => lambda { has_group == 1 })
|
21
21
|
end
|
@@ -23,21 +23,24 @@ module Mp3file::ID3v2
|
|
23
23
|
class ID3v240FrameHeaderFormat < BinData::Record
|
24
24
|
string(:frame_id, :length => 4)
|
25
25
|
uint32be(:frame_size)
|
26
|
-
bit1(:unused1, :check_value => lambda { value == 0 })
|
26
|
+
bit1(:unused1) # , :check_value => lambda { value == 0 })
|
27
27
|
bit1(:tag_alter_preserve)
|
28
28
|
bit1(:file_alter_preserve)
|
29
29
|
bit1(:read_only)
|
30
|
-
|
31
|
-
|
30
|
+
|
31
|
+
bit4(:unused2) # , :check_value => lambda { value == 0 })
|
32
|
+
|
33
|
+
bit1(:unused3) # , :check_value => lambda { value == 0 })
|
32
34
|
bit1(:group)
|
33
|
-
bit2(:unused4, :check_value => lambda { value == 0 })
|
35
|
+
bit2(:unused4) # , :check_value => lambda { value == 0 })
|
36
|
+
|
34
37
|
bit1(:compression)
|
35
38
|
bit1(:encryption)
|
36
39
|
bit1(:unsynchronized)
|
37
40
|
bit1(:data_length_indicator)
|
38
41
|
end
|
39
42
|
|
40
|
-
attr_reader(:frame_id, :size,
|
43
|
+
attr_reader(:frame_id, :header_size, :frame_size, :size,
|
41
44
|
:preserve_on_altered_tag, :preserve_on_altered_file,
|
42
45
|
:read_only, :compressed, :encrypted, :encryption_type,
|
43
46
|
:group, :unsynchronized, :data_length)
|
@@ -57,8 +60,10 @@ module Mp3file::ID3v2
|
|
57
60
|
begin
|
58
61
|
if @tag.version >= ID3V2_2_0 && @tag.version < ID3V2_3_0
|
59
62
|
header = ID3v220FrameHeaderFormat.read(io)
|
63
|
+
@header_size = 6
|
60
64
|
elsif @tag.version >= ID3V2_3_0 && @tag.version < ID3V2_4_0
|
61
65
|
header = ID3v230FrameHeaderFormat.read(io)
|
66
|
+
@header_size = 10
|
62
67
|
@preserve_on_altered_tag = header.tag_alter_preserve == 1
|
63
68
|
@preserve_on_altered_file = header.file_alter_preserve == 1
|
64
69
|
@read_only = header.read_only == 1
|
@@ -72,13 +77,15 @@ module Mp3file::ID3v2
|
|
72
77
|
end
|
73
78
|
elsif @tag.version >= ID3V2_4_0
|
74
79
|
header = ID3v240FrameHeaderFormat.read(io)
|
80
|
+
@header_size = 10
|
75
81
|
end
|
76
82
|
rescue BinData::ValidityError => ve
|
77
83
|
raise InvalidID3v2TagError, ve.message
|
78
84
|
end
|
79
85
|
|
80
86
|
@frame_id = header.frame_id
|
81
|
-
@
|
87
|
+
@frame_size = BitPaddedInt.unpad_number(header.frame_size)
|
88
|
+
@size = @header_size + @frame_size
|
82
89
|
end
|
83
90
|
end
|
84
91
|
end
|
data/lib/mp3file/id3v2/tag.rb
CHANGED
@@ -7,19 +7,53 @@ module Mp3file::ID3v2
|
|
7
7
|
def_delegators(:@header, :version, :unsynchronized, :extended_header,
|
8
8
|
:compression, :experimental, :footer)
|
9
9
|
|
10
|
-
attr_reader(:header, :frames)
|
10
|
+
attr_reader(:header, :frames, :unused_bytes)
|
11
|
+
|
12
|
+
FRAME_HEADER_NAME_REGEX = /[A-Z][A-Z0-9]{2,3}/
|
11
13
|
|
12
14
|
def initialize(io)
|
13
15
|
@header = Header.new(io)
|
14
|
-
|
16
|
+
load_frames(io)
|
17
|
+
used_bytes = @frames.inject(0) { |m, f| m += f.size }
|
18
|
+
@unused_bytes = @header.tag_size - used_bytes
|
15
19
|
end
|
16
20
|
|
17
|
-
def load_frames
|
21
|
+
def load_frames(io)
|
18
22
|
@frames = []
|
23
|
+
|
24
|
+
data = io.read(@header.tag_size)
|
25
|
+
data.force_encoding("ASCII-8BIT")
|
26
|
+
offset = 0
|
27
|
+
|
28
|
+
frame_offset, frame = get_next_frame_header(data, offset)
|
29
|
+
while frame
|
30
|
+
@frames << frame
|
31
|
+
offset = frame_offset + frame.size
|
32
|
+
frame_offset, frame = get_next_frame_header(data, offset)
|
33
|
+
end
|
19
34
|
end
|
20
35
|
|
21
36
|
def size
|
22
37
|
@header.tag_size + 10
|
23
38
|
end
|
39
|
+
|
40
|
+
def get_next_frame_header(data, offset)
|
41
|
+
md = data.match(FRAME_HEADER_NAME_REGEX, offset)
|
42
|
+
|
43
|
+
while md
|
44
|
+
frame_offset = md.begin(0)
|
45
|
+
io = StringIO.new(data[frame_offset, 32])
|
46
|
+
frame = FrameHeader.new(io, self)
|
47
|
+
|
48
|
+
if frame.frame_id.to_s =~ FRAME_HEADER_NAME_REGEX
|
49
|
+
return [ frame_offset, frame ]
|
50
|
+
else
|
51
|
+
frame_offset = md.end(0) + 1
|
52
|
+
md = data.match(FRAME_HEADER_NAME_REGEX, frame_offset)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
[ nil, nil ]
|
57
|
+
end
|
24
58
|
end
|
25
59
|
end
|
data/lib/mp3file/mp3_file.rb
CHANGED
@@ -9,7 +9,7 @@ module Mp3file
|
|
9
9
|
attr_reader(:mpeg_version, :layer, :bitrate, :samplerate, :mode)
|
10
10
|
attr_reader(:num_frames, :total_samples, :length)
|
11
11
|
|
12
|
-
attr_accessor(:id3v1_tag)
|
12
|
+
attr_accessor(:id3v1_tag, :id3v2_tag, :extra_id3v2_tags)
|
13
13
|
|
14
14
|
def initialize(file_path)
|
15
15
|
file_path = Pathname.new(file_path).expand_path if file_path.is_a?(String)
|
@@ -73,30 +73,57 @@ module Mp3file
|
|
73
73
|
|
74
74
|
# Try to read an ID3v1 tag.
|
75
75
|
@id3v1_tag = nil
|
76
|
-
@file.seek(-128, IO::SEEK_END)
|
77
76
|
begin
|
78
|
-
@
|
79
|
-
|
77
|
+
@file.seek(-128, IO::SEEK_END)
|
78
|
+
@id3v1_tag = ID3v1Tag.parse(@file)
|
79
|
+
rescue Mp3file::InvalidID3v1TagError
|
80
80
|
@id3v1_tag = nil
|
81
|
+
ensure
|
82
|
+
@file.seek(0, IO::SEEK_SET)
|
81
83
|
end
|
82
|
-
@file.seek(0, IO::SEEK_SET)
|
83
84
|
|
84
85
|
# Try to detect an ID3v2 header.
|
85
|
-
@
|
86
|
+
@id3v2_tag = nil
|
86
87
|
begin
|
87
|
-
@
|
88
|
-
rescue ID3v2::InvalidID3v2TagError => e
|
89
|
-
|
88
|
+
@id3v2_tag = ID3v2::Tag.new(@file)
|
89
|
+
rescue ID3v2::InvalidID3v2TagError # => e
|
90
|
+
# $stderr.puts "Error parsing ID3v2 tag: %s\n\t%s" %
|
91
|
+
# [ e.message, e.backtrace.join("\n\t") ]
|
92
|
+
@id3v2_tag = nil
|
90
93
|
@file.seek(0, IO::SEEK_SET)
|
91
94
|
end
|
92
95
|
|
93
96
|
# Skip past the ID3v2 header if it's present.
|
94
|
-
if @
|
95
|
-
@file.seek(@
|
97
|
+
if @id3v2_tag
|
98
|
+
@file.seek(@id3v2_tag.size, IO::SEEK_SET)
|
96
99
|
end
|
97
100
|
|
98
|
-
#
|
99
|
-
|
101
|
+
# Some files have more than one ID3v2 tag. If we can't find an
|
102
|
+
# MP3 header in the next 4k, try reading another ID3v2 tag and
|
103
|
+
# repeat.
|
104
|
+
@extra_id3v2_tags = []
|
105
|
+
begin
|
106
|
+
# Try to find the first MP3 header.
|
107
|
+
@first_header_offset, @first_header = get_next_header(@file)
|
108
|
+
rescue InvalidMP3FileError
|
109
|
+
end_of_tags = @id3v2_tag.size + @extra_id3v2_tags.map(&:last).map(&:size).reduce(:+).to_i
|
110
|
+
@file.seek(end_of_tags, IO::SEEK_SET)
|
111
|
+
|
112
|
+
tag = nil
|
113
|
+
begin
|
114
|
+
tag = ID3v2::Tag.new(@file)
|
115
|
+
rescue ID3v2::InvalidID3v2TagError
|
116
|
+
tag = nil
|
117
|
+
@file.seek(end_of_tags, IO::SEEK_SET)
|
118
|
+
end
|
119
|
+
|
120
|
+
if tag
|
121
|
+
@extra_id3v2_tags << [ end_of_tags, tag ]
|
122
|
+
retry
|
123
|
+
else
|
124
|
+
raise
|
125
|
+
end
|
126
|
+
end
|
100
127
|
|
101
128
|
@mpeg_version = @first_header.version
|
102
129
|
@layer = @first_header.layer
|
@@ -117,7 +144,7 @@ module Mp3file
|
|
117
144
|
@file.seek(@first_header.side_bytes, IO::SEEK_CUR)
|
118
145
|
begin
|
119
146
|
@xing_header = XingHeader.new(@file)
|
120
|
-
rescue InvalidXingHeaderError
|
147
|
+
rescue InvalidXingHeaderError
|
121
148
|
@file.seek(@first_header_offset + 4, IO::SEEK_CUR)
|
122
149
|
end
|
123
150
|
|
@@ -155,7 +182,7 @@ module Mp3file
|
|
155
182
|
begin
|
156
183
|
header = MP3Header.new(file)
|
157
184
|
header_offset = file.tell - 4
|
158
|
-
rescue InvalidMP3HeaderError
|
185
|
+
rescue InvalidMP3HeaderError
|
159
186
|
header_offset += 1
|
160
187
|
if header_offset - initial_header_offset > 4096
|
161
188
|
raise InvalidMP3FileError, "Could not find a valid MP3 header in the first 4096 bytes."
|
data/lib/mp3file/version.rb
CHANGED
@@ -5,7 +5,7 @@ include CommonHelpers
|
|
5
5
|
|
6
6
|
describe Mp3file::ID3v1Tag do
|
7
7
|
it "rejects an ID3v1 tag if it doesn't begin with TAG" do
|
8
|
-
lambda { Mp3file::ID3v1Tag.
|
8
|
+
lambda { Mp3file::ID3v1Tag.parse(StringIO.new("\x00" * 128)) }.
|
9
9
|
should(raise_error(Mp3file::InvalidID3v1TagError))
|
10
10
|
end
|
11
11
|
|
@@ -17,7 +17,7 @@ describe Mp3file::ID3v1Tag do
|
|
17
17
|
year = "1996"
|
18
18
|
comment = "This is a comment"; comment += "\x00" * (30 - comment.size)
|
19
19
|
genre = 17.chr
|
20
|
-
Mp3file::ID3v1Tag.
|
20
|
+
Mp3file::ID3v1Tag.parse(StringIO.new('TAG' + title + artist + album + year + comment + genre))
|
21
21
|
end
|
22
22
|
|
23
23
|
its(:title) { should == 'Big Dipper' }
|
@@ -38,7 +38,7 @@ describe Mp3file::ID3v1Tag do
|
|
38
38
|
comment = "This is a comment"; comment += "\x00" * (29 - comment.size)
|
39
39
|
tracknum = 3.chr
|
40
40
|
genre = 17.chr
|
41
|
-
Mp3file::ID3v1Tag.
|
41
|
+
Mp3file::ID3v1Tag.parse(StringIO.new('TAG' + title + artist + album + year + comment + tracknum + genre))
|
42
42
|
end
|
43
43
|
|
44
44
|
its(:title) { should == 'Big Dipper' }
|
@@ -51,7 +51,7 @@ describe Mp3file::ID3v1Tag do
|
|
51
51
|
end
|
52
52
|
|
53
53
|
describe "When created with a blank ID3v1 tag" do
|
54
|
-
subject { Mp3file::ID3v1Tag.
|
54
|
+
subject { Mp3file::ID3v1Tag.parse(StringIO.new("TAG" + ("\x00" * 125))) }
|
55
55
|
its(:title) { should == nil }
|
56
56
|
its(:artist) { should == nil }
|
57
57
|
its(:album) { should == nil }
|
@@ -26,13 +26,13 @@ describe Mp3file::ID3v2::FrameHeader do
|
|
26
26
|
context "with ID3v2.3 tags" do
|
27
27
|
let(:tag) { Mp3file::ID3v2::Tag.new(StringIO.new("ID3\x03\x00\x00\x00\x00\x00\x00")) }
|
28
28
|
|
29
|
-
describe("A header with invalid flag bits set") do
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
end
|
29
|
+
# describe("A header with invalid flag bits set") do
|
30
|
+
# it("Should raise an error") do
|
31
|
+
# io = StringIO.new("TIT2\x00\x00\x00\x09\x01\x00")
|
32
|
+
# lambda { Mp3file::ID3v2::FrameHeader.new(io, tag) }.
|
33
|
+
# should(raise_error(Mp3file::ID3v2::InvalidID3v2TagError))
|
34
|
+
# end
|
35
|
+
# end
|
36
36
|
|
37
37
|
describe("A 9-byte TIT2 frame header.") do
|
38
38
|
subject { Mp3file::ID3v2::FrameHeader.new(StringIO.new("TIT2\x00\x00\x00\x09\x00\x00"), tag) }
|
@@ -68,12 +68,12 @@ describe Mp3file::ID3v2::FrameHeader do
|
|
68
68
|
context "with ID3v2.4 tags" do
|
69
69
|
let(:tag) { Mp3file::ID3v2::Tag.new(StringIO.new("ID3\x04\x00\x00\x00\x00\x00\x00")) }
|
70
70
|
|
71
|
-
describe("A header with invalid flag bits set") do
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
end
|
71
|
+
# describe("A header with invalid flag bits set") do
|
72
|
+
# it("Should raise an error") do
|
73
|
+
# io = StringIO.new("TIT2\x00\x00\x00\x09\x01\x00")
|
74
|
+
# lambda { Mp3file::ID3v2::FrameHeader.new(io, tag) }.
|
75
|
+
# should(raise_error(Mp3file::ID3v2::InvalidID3v2TagError))
|
76
|
+
# end
|
77
|
+
# end
|
78
78
|
end
|
79
79
|
end
|
@@ -5,11 +5,7 @@ include CommonHelpers
|
|
5
5
|
|
6
6
|
describe Mp3file::ID3v2::Tag do
|
7
7
|
describe "An empty tag" do
|
8
|
-
subject
|
9
|
-
t = Mp3file::ID3v2::Tag.new(StringIO.new("ID3\x03\x00\x00\x00\x00\x00\x00"))
|
10
|
-
t.load_frames
|
11
|
-
t
|
12
|
-
end
|
8
|
+
subject { Mp3file::ID3v2::Tag.new(StringIO.new("ID3\x03\x00\x00\x00\x00\x00\x00")) }
|
13
9
|
its(:version) { should == Mp3file::ID3v2::ID3V2_3_0 }
|
14
10
|
its(:unsynchronized) { should == false }
|
15
11
|
its(:extended_header) { should == false }
|
@@ -70,7 +70,7 @@ describe Mp3file::MP3File do
|
|
70
70
|
|
71
71
|
describe "A 96 kbps 34 kHz Joint Stereo CBR file with an ID3v2 tag" do
|
72
72
|
subject { Mp3file::MP3File.new(fixture_file('bret_id3v2.mp3')) }
|
73
|
-
|
73
|
+
its(:id3v2tag?) { should == true }
|
74
74
|
its(:id3v1tag?) { should == false }
|
75
75
|
its("file.path") { should == fixture_file('bret_id3v2.mp3').to_s }
|
76
76
|
its("file.closed?") { should == true }
|
metadata
CHANGED
@@ -1,62 +1,74 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mp3file
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
5
|
-
prerelease:
|
4
|
+
version: 1.0.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Andrew Watts
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
13
|
-
default_executable:
|
11
|
+
date: 2014-11-05 00:00:00.000000000 Z
|
14
12
|
dependencies:
|
15
13
|
- !ruby/object:Gem::Dependency
|
16
14
|
name: rspec
|
17
|
-
requirement:
|
18
|
-
none: false
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
19
16
|
requirements:
|
20
|
-
- -
|
17
|
+
- - ">="
|
21
18
|
- !ruby/object:Gem::Version
|
22
19
|
version: '0'
|
23
20
|
type: :development
|
24
21
|
prerelease: false
|
25
|
-
version_requirements:
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
26
27
|
- !ruby/object:Gem::Dependency
|
27
28
|
name: rake
|
28
|
-
requirement:
|
29
|
-
none: false
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
|
-
version_requirements:
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
37
41
|
- !ruby/object:Gem::Dependency
|
38
42
|
name: bindata
|
39
|
-
requirement:
|
40
|
-
none: false
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
41
44
|
requirements:
|
42
|
-
- -
|
45
|
+
- - ">="
|
43
46
|
- !ruby/object:Gem::Version
|
44
47
|
version: '0'
|
45
48
|
type: :runtime
|
46
49
|
prerelease: false
|
47
|
-
version_requirements:
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
48
55
|
description: Reads MP3 headers and returns their information.
|
49
56
|
email:
|
50
57
|
- ahwatts@gmail.com
|
51
|
-
executables:
|
58
|
+
executables:
|
59
|
+
- mp3info
|
52
60
|
extensions: []
|
53
61
|
extra_rdoc_files: []
|
54
62
|
files:
|
55
|
-
- .
|
56
|
-
- .
|
63
|
+
- ".gitattributes"
|
64
|
+
- ".gitignore"
|
65
|
+
- ".ruby-gemset"
|
66
|
+
- ".ruby-version"
|
57
67
|
- Gemfile
|
58
68
|
- Gemfile.lock
|
69
|
+
- README.md
|
59
70
|
- Rakefile
|
71
|
+
- bin/mp3info
|
60
72
|
- doc/id3v2.2.0.txt
|
61
73
|
- doc/id3v2.3.0.txt
|
62
74
|
- doc/id3v2.4.0-frames.txt
|
@@ -90,29 +102,42 @@ files:
|
|
90
102
|
- spec/mp3file/mp3_file_spec.rb
|
91
103
|
- spec/mp3file/mp3_header_spec.rb
|
92
104
|
- spec/mp3file/xing_header_spec.rb
|
93
|
-
has_rdoc: true
|
94
105
|
homepage: http://rubygems.org/gems/mp3file
|
95
106
|
licenses: []
|
107
|
+
metadata: {}
|
96
108
|
post_install_message:
|
97
109
|
rdoc_options: []
|
98
110
|
require_paths:
|
99
111
|
- lib
|
100
112
|
required_ruby_version: !ruby/object:Gem::Requirement
|
101
|
-
none: false
|
102
113
|
requirements:
|
103
|
-
- -
|
114
|
+
- - ">="
|
104
115
|
- !ruby/object:Gem::Version
|
105
116
|
version: '0'
|
106
117
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
107
|
-
none: false
|
108
118
|
requirements:
|
109
|
-
- -
|
119
|
+
- - ">="
|
110
120
|
- !ruby/object:Gem::Version
|
111
121
|
version: '0'
|
112
122
|
requirements: []
|
113
123
|
rubyforge_project: mp3file
|
114
|
-
rubygems_version:
|
124
|
+
rubygems_version: 2.4.2
|
115
125
|
signing_key:
|
116
|
-
specification_version:
|
126
|
+
specification_version: 4
|
117
127
|
summary: Reads MP3 headers and returns their information.
|
118
|
-
test_files:
|
128
|
+
test_files:
|
129
|
+
- spec/common_helpers.rb
|
130
|
+
- spec/files/bret_96.mp3
|
131
|
+
- spec/files/bret_id3v1.mp3
|
132
|
+
- spec/files/bret_id3v2.mp3
|
133
|
+
- spec/files/bret_vbr_6.mp3
|
134
|
+
- spec/files/zeroes.mp3
|
135
|
+
- spec/mp3file/id3v1_tag_spec.rb
|
136
|
+
- spec/mp3file/id3v2/bit_padded_int_spec.rb
|
137
|
+
- spec/mp3file/id3v2/frame_header_spec.rb
|
138
|
+
- spec/mp3file/id3v2/header_spec.rb
|
139
|
+
- spec/mp3file/id3v2/tag_spec.rb
|
140
|
+
- spec/mp3file/id3v2/version_spec.rb
|
141
|
+
- spec/mp3file/mp3_file_spec.rb
|
142
|
+
- spec/mp3file/mp3_header_spec.rb
|
143
|
+
- spec/mp3file/xing_header_spec.rb
|
data/.rvmrc
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
rvm 1.9.2@mp3file
|