mp3file 0.0.4 → 1.0.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 +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
|