Class: ID3v2

### Home Classes Methods

### In Files

- lib/mp3info/id3v2.rb

### Parent

DelegateClass(Hash)

### Methods

- ::new
- #add_picture
- #changed?
- #from_io
- #inspect
- #parsed?
- #pictures
- #remove_pictures
- #to_bin
- #to_inspect_hash
- #version

### Included Modules

- Kernel

### Files

- History.txt
- Manifest.txt

### Class/Module Index

Quicksearch

- ID3v2
- ID3v2Error
- Mp3Info
- Mp3InfoError
- Object
- String

No matching classes.

# ID3v2

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.

### Constants

TAGS
TAG_MAPPING_2_2_to_2_3

  Translate V2 to V3 tags

TEXT_ENCODINGS

  See id3v2.4.0-structure document, at section 4.

### Attributes

io_position[R]

this is the position in the file where the tag really ends

options[R]

:`lang`: for writing comments

DEPRECATION

  :`encoding`: one of the string of `TEXT_ENCODINGS`,

use of :encoding parameter is DEPRECATED. In ruby 1.8, use utf-8 encoded strings for tags. In ruby >= 1.9, strings are automatically transcoded from their originaloriginal encoding.

### Public Class Methods

new(options = {}) click to toggle source

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 184
    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

### Public Instance Methods

add_picture(data, opts = {}) click to toggle source

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 245
    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

changed?() click to toggle source

does this tag has been changed ?

    # File lib/mp3info/id3v2.rb, line 207
    def changed?
      @hash_orig != @hash
    end

from_io(io) click to toggle source

gets id3v2 tag information from io object (must support seek() method)

    # File lib/mp3info/id3v2.rb, line 352
    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

inspect() click to toggle source

    # File lib/mp3info/id3v2.rb, line 342
    def inspect
      self.to_inspect_hash
    end

parsed?() click to toggle source

does this tag has been correctly read ?

    # File lib/mp3info/id3v2.rb, line 202
    def parsed?
      @parsed
    end

pictures() click to toggle source

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 278
    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
        _, _, 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

remove_pictures() click to toggle source

    # File lib/mp3info/id3v2.rb, line 346
    def remove_pictures
      self["APIC"] = ""
      self["PIC"]  = ""
    end

to_bin() click to toggle source

dump tag for writing. Version is always 2.3.0

    # File lib/mp3info/id3v2.rb, line 384
    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

to_inspect_hash() click to toggle source

cuts out long tag values from hash for display on screen

    # File lib/mp3info/id3v2.rb, line 222
    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

version() click to toggle source

full version of this tag (like "2.3.0") or nil if tag was not correctly read

    # File lib/mp3info/id3v2.rb, line 213
    def version
      if @version_maj && @version_min
        "2.#{@version_maj}.#{@version_min}"
      else
        nil
      end
    end

[Validate]

Generated with the Darkfish Rdoc Generator 2.