Parent

Class/Module Index [+]

Quicksearch

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

APIC
PIC
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 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

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 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
changed?() click to toggle source

does this tag has been changed ?

# File lib/mp3info/id3v2.rb, line 205
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 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
inspect() click to toggle source
# File lib/mp3info/id3v2.rb, line 340
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 200
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 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
remove_pictures() click to toggle source
# File lib/mp3info/id3v2.rb, line 344
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 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
to_inspect_hash() click to toggle source

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
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 211
def version
  if @version_maj && @version_min
    "2.#{@version_maj}.#{@version_min}"
  else
    nil
  end
end

[Validate]

Generated with the Darkfish Rdoc Generator 2.