DelegateClass(Hash)
This class can be used to decode id3v2 tags from files, like .mp3 or .ape for example. It works like a hash, where key represents the tag name as 3 or 4 upper case letters (respectively related to 2.2 and 2.3+ tag) and value represented as array or raw value. Written version is always 2.3.
Translate V2 to V3 tags
See id3v2.4.0-structure document, at section 4.
possible options are described above ('options' attribute) you can access this object like an hash, with [] and []= methods special cases are ["disc_number"] and ["disc_total"] mirroring TPOS attribute
# File lib/mp3info/id3v2.rb, line 182 def initialize(options = {}) @options = { :lang => "ENG" } if @options[:encoding] warn("use of :encoding parameter is DEPRECATED. In ruby 1.8, use utf-8 encoded strings for tags.\n" + "In ruby >= 1.9, strings are automatically transcoded from their original encoding.") end @options.update(options) @hash = {} #TAGS.keys.each { |k| @hash[k] = nil } @hash_orig = {} super(@hash) @parsed = false @version_maj = @version_min = nil end
ID3V2::add_picture Takes an image string as input and writes it with header. Mime type is automatically guessed by default. It is possible but not necessary to include:
:pic_type => 0 - 14 (see http://id3.org/id3v2.3.0#Attached_picture) :mime => 'gif' :description => "Image description"
# File lib/mp3info/id3v2.rb, line 243 def add_picture(data, opts = {}) options = { :pic_type => 0, :mime => nil, :description => "image" } options.update(opts) jpg = Regexp.new( "^\xFF".force_encoding("BINARY"), Regexp::FIXEDENCODING ) png = Regexp.new( "^\x89PNG".force_encoding("BINARY"), Regexp::FIXEDENCODING ) gif = Regexp.new( "^\x89GIF".force_encoding("BINARY"), Regexp::FIXEDENCODING ) mime = options[:mime] mime ||= "jpg" if data.match jpg mime ||= "png" if data.match png mime ||= "gif" if data.match gif pic_type = options[:pic_type] pic_type = ["%02i" % pic_type].pack('H*') desc = "#{options[:description]}" header = "\x00image/#{mime}\x00#{pic_type}#{desc}\x00" self["APIC"] = header + data.force_encoding('BINARY') end
does this tag has been changed ?
# File lib/mp3info/id3v2.rb, line 205 def changed? @hash_orig != @hash end
gets id3v2 tag information from io object (must support seek() method)
# File lib/mp3info/id3v2.rb, line 350 def from_io(io) @io = io original_pos = @io.pos @io.extend(Mp3Info::Mp3FileMethods) version_maj, version_min, flags = @io.read(3).unpack("CCB4") @unsync, ext_header, _, _ = (0..3).collect { |i| flags[i].chr == '1' } # _, _ = experimental, footer raise(ID3v2Error, "can't find version_maj ('#{version_maj}')") unless [2, 3, 4].include?(version_maj) @version_maj, @version_min = version_maj, version_min @tag_length = @io.get_syncsafe @parsed = true begin case @version_maj when 2 read_id3v2_2_frames when 3, 4 # seek past extended header if present @io.seek(@io.get_syncsafe - 4, IO::SEEK_CUR) if ext_header read_id3v2_3_frames end rescue ID3v2Error => e warn("warning: id3v2 tag not fully parsed: #{e.message}") end @io_position = @io.pos @tag_length = @io_position - original_pos @hash_orig = @hash.dup #no more reading @io = nil end
# File lib/mp3info/id3v2.rb, line 340 def inspect self.to_inspect_hash end
does this tag has been correctly read ?
# File lib/mp3info/id3v2.rb, line 200 def parsed? @parsed end
Returns an array of images:
[ ["01_.jpg", "Image Data in Binary String"], ["02_.png", "Another Image in a String"] ]
e.g. to write all images: mp3.tag2.pictures.each do |image|
File.open(img[0], 'wb'){|f| f.write img[1])
end
# File lib/mp3info/id3v2.rb, line 276 def pictures apic_images = [self["APIC"]].flatten.dup result = [] apic_images.each_index do |index| pic = apic_images[index] next if !pic.is_a?(String) or pic == "" pic.force_encoding 'BINARY' picture = [] jpg = Regexp.new("jpg|JPG|jpeg|JPEG".force_encoding("BINARY"), Regexp::FIXEDENCODING ) png = Regexp.new("png|PNG".force_encoding("BINARY"), Regexp::FIXEDENCODING ) header = pic.unpack('a120').first.force_encoding "BINARY" mime_pos = 0 # safest way to correctly extract jpg and png is finding mime if header.match jpg mime = "jpg" mime_pos = header =~ jpg start = Regexp.new("^\3377".force_encoding("BINARY"), Regexp::FIXEDENCODING ) elsif header.match png mime = "png" mime_pos = header =~ png start = Regexp.new("^\x89PNG".force_encoding("BINARY"), Regexp::FIXEDENCODING ) else mime = "dat" end puts "analysing image: #{header.inspect}..." if $DEBUG mim, pic_type, desc, data = pic[mime_pos, pic.length].unpack('Z*hZ*a*') if mime != "dat" and (!data.match(start) or data.nil?) real_start = pic =~ start data = pic[real_start, pic.length] end if mime == "dat" # if no jpg or png, extract data anyway e.g. gif mime, desc, data = pic.unpack('h Z* h Z* a*').values_at(1,3,4) end if mime == "jpg" # inspect jpg image header (first 10 chars) for "\xFF\x00" (expect "\xFF") trailing_null_byte = Regexp.new("(\3377)(\0000)".force_encoding('BINARY'), Regexp::FIXEDENCODING) if (data =~ trailing_null_byte) < 10 data.gsub!(trailing_null_byte, "\xff".force_encoding('BINARY')) end end desc = "%02i_#{desc[0,25]}" % (index + 1) filename = desc.match("#{mime}$") ? desc : "#{desc}.#{mime}" filename.gsub!('/','') picture[0] = filename picture[1] = data result << picture end result end
# File lib/mp3info/id3v2.rb, line 344 def remove_pictures self.APIC = "" self.PIC = "" end
dump tag for writing. Version is always 2.3.0
# File lib/mp3info/id3v2.rb, line 382 def to_bin #TODO handle of @tag2[TLEN"] #TODO add of crc #TODO add restrictions tag tag = "" @hash.each do |k, v| next unless v next if v.respond_to?("empty?") and v.empty? # Automagically translate V2 to V3 tags k = TAG_MAPPING_2_2_to_2_3[k] if TAG_MAPPING_2_2_to_2_3.has_key?(k) # doesn't encode id3v2.2 tags, which have 3 characters next if k.size != 4 # Output one flag for each array element, or one only if it's not an array [v].flatten.each do |value| data = encode_tag(k, value.to_s) #data << "\x00"*2 #End of tag tag << k[0,4] #4 characte max for a tag's key #tag << to_syncsafe(data.size) #+1 because of the language encoding byte size = data.size unless RUBY_1_8 size = data.dup.force_encoding("binary").size end tag << [size].pack("N") #+1 because of the language encoding byte tag << "\x00"*2 #flags tag << data end end tag_str = "ID3" #version_maj, version_min, unsync, ext_header, experimental, footer tag_str << [ 3, 0, "0000" ].pack("CCB4") tag_str << [to_syncsafe(tag.size)].pack("N") tag_str << tag puts "tag in binary format: #{tag_str.inspect}" if $DEBUG tag_str end
cuts out long tag values from hash for display on screen
# File lib/mp3info/id3v2.rb, line 220 def to_inspect_hash result = Marshal.load(Marshal.dump(self.to_hash)) result.each do |k,v| if v.is_a? Array v.each_index do |i, item| if (v[i].is_a? String and v[i].length > 128) result[k][i] = pretty_header(v[i]) end end elsif v.is_a? String and v.length > 128 result[k] = pretty_header(v) # this method 'snips' long data end end result end
Generated with the Darkfish Rdoc Generator 2.