lib/mp3file/mp3_file.rb in mp3file-0.0.4 vs lib/mp3file/mp3_file.rb in mp3file-1.0.0

- old
+ new

@@ -7,11 +7,11 @@ attr_reader(:xing_header_offset, :xing_header) attr_reader(:vbri_header_offset, :vbri_header) attr_reader(:mpeg_version, :layer, :bitrate, :samplerate, :mode) attr_reader(:num_frames, :total_samples, :length) - attr_accessor(:id3v1_tag) + attr_accessor(:id3v1_tag, :id3v2_tag, :extra_id3v2_tags) def initialize(file_path) file_path = Pathname.new(file_path).expand_path if file_path.is_a?(String) load_file(file_path) end @@ -71,35 +71,62 @@ @file.seek(0, IO::SEEK_END) @file_size = @file.tell # Try to read an ID3v1 tag. @id3v1_tag = nil - @file.seek(-128, IO::SEEK_END) begin - @id3v1_tag = ID3v1Tag.new(@file) - rescue InvalidID3v1TagError => e + @file.seek(-128, IO::SEEK_END) + @id3v1_tag = ID3v1Tag.parse(@file) + rescue Mp3file::InvalidID3v1TagError @id3v1_tag = nil + ensure + @file.seek(0, IO::SEEK_SET) end - @file.seek(0, IO::SEEK_SET) # Try to detect an ID3v2 header. - @id3v2_header = nil + @id3v2_tag = nil begin - @id3v2_header = ID3v2::Header.new(@file) - rescue ID3v2::InvalidID3v2TagError => e - @id3v2_header = nil + @id3v2_tag = ID3v2::Tag.new(@file) + rescue ID3v2::InvalidID3v2TagError # => e + # $stderr.puts "Error parsing ID3v2 tag: %s\n\t%s" % + # [ e.message, e.backtrace.join("\n\t") ] + @id3v2_tag = nil @file.seek(0, IO::SEEK_SET) end # Skip past the ID3v2 header if it's present. - if @id3v2_header - @file.seek(@id3v2_header.tag_size + 10, IO::SEEK_SET) + if @id3v2_tag + @file.seek(@id3v2_tag.size, IO::SEEK_SET) end - # Try to find the first MP3 header. - @first_header_offset, @first_header = get_next_header(@file) + # Some files have more than one ID3v2 tag. If we can't find an + # MP3 header in the next 4k, try reading another ID3v2 tag and + # repeat. + @extra_id3v2_tags = [] + begin + # Try to find the first MP3 header. + @first_header_offset, @first_header = get_next_header(@file) + rescue InvalidMP3FileError + end_of_tags = @id3v2_tag.size + @extra_id3v2_tags.map(&:last).map(&:size).reduce(:+).to_i + @file.seek(end_of_tags, IO::SEEK_SET) + tag = nil + begin + tag = ID3v2::Tag.new(@file) + rescue ID3v2::InvalidID3v2TagError + tag = nil + @file.seek(end_of_tags, IO::SEEK_SET) + end + + if tag + @extra_id3v2_tags << [ end_of_tags, tag ] + retry + else + raise + end + end + @mpeg_version = @first_header.version @layer = @first_header.layer @bitrate = @first_header.bitrate / 1000 @samplerate = @first_header.samplerate @mode = @first_header.mode @@ -115,11 +142,11 @@ # side_bytes. @xing_header = nil @file.seek(@first_header.side_bytes, IO::SEEK_CUR) begin @xing_header = XingHeader.new(@file) - rescue InvalidXingHeaderError => ve + rescue InvalidXingHeaderError @file.seek(@first_header_offset + 4, IO::SEEK_CUR) end if @xing_header @vbr = true @@ -153,10 +180,10 @@ while header.nil? begin header = MP3Header.new(file) header_offset = file.tell - 4 - rescue InvalidMP3HeaderError => e + rescue InvalidMP3HeaderError header_offset += 1 if header_offset - initial_header_offset > 4096 raise InvalidMP3FileError, "Could not find a valid MP3 header in the first 4096 bytes." else file.seek(header_offset, IO::SEEK_SET)