Class: ID3::Tag2

Inherits:
GenericTag show all
Defined in:
lib/id3/tag2.rb

Overview

Class Tag2 ID3 Version 2.x.y Tag

parses ID3v2 tags from a binary array
dumps  ID3v2 tags into a binary array
allows to modify tag's contents

as per definition, the frames are in no fixed order

Instance Attribute Summary collapse

Attributes inherited from GenericTag

#raw, #version

Attributes inherited from RestrictedOrderedHash

#locked

Instance Method Summary collapse

Methods inherited from RestrictedOrderedHash

#delete, #lock, #old_delete, #old_store

Constructor Details

#initializeTag2

Returns a new instance of Tag2.


15
16
17
18
19
20
# File 'lib/id3/tag2.rb', line 15

def initialize
  super
  @rawflags = 0
  @flags    = {}
  @version  = '2.3.0'   # default version
end

Instance Attribute Details

#flagsObject (readonly)

Returns the value of attribute flags


13
14
15
# File 'lib/id3/tag2.rb', line 13

def flags
  @flags
end

#rawflagsObject (readonly)

Returns the value of attribute rawflags


13
14
15
# File 'lib/id3/tag2.rb', line 13

def rawflags
  @rawflags
end

Instance Method Details

#[]=(framename, val) ⇒ Object

this is obviously half-baked.. does not really work! We need SubClasses of ID3::Frame with specific handling


24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/id3/tag2.rb', line 24

def []=(framename,val)
  # if this is a valid frame of known type, we return it's total length and a struct
  #
  if ID3::SUPPORTED_SYMBOLS[@version].has_value?(framename)
    frame = ID3::Frame.new( framename, @version)
    self[ framename ] = frame
    frame['text'] = val  if frame.has_key?('text')
    return frame
  else
    return nil
  end
end

#dumpObject


236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
# File 'lib/id3/tag2.rb', line 236

def dump
  data = ""

  # dump all the frames
  self.each { |framename,framedata|
    data << framedata.dump
  }  # add some padding perhaps 32 bytes (should be defined by the user!)
  # NOTE:    I noticed that iTunes adds excessive amounts of padding

  data << ZEROBYTE * 32
  
  # calculate the complete length of the data-section 
  size = mungeSize(data.size)
  
  major,minor = @version.sub(/^2\.([0-9])\.([0-9])/, '\1 \2').split
  
  # prepend a valid ID3-v2.x header to the data block
  header = "ID3" << major.to_i << minor.to_i << @rawflags << size[0] << size[1] << size[2] << size[3]
  
  header + data
end

#read_from_buffer(string) ⇒ Object


37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/id3/tag2.rb', line 37

def read_from_buffer(string)
  has_tag = string =~ /^ID3/
  if has_tag
    major = string.getbyte(ID3::ID3v2major)
    minor = string.getbyte(ID3::ID3v2minor)
    @version  = "2." + major.to_s + '.' + minor.to_s
    @rawflags = string.getbyte(ID3::ID3v2flags)
    size =  ID3::ID3v2headerSize + ID3.unmungeSize( string[ID3::ID3v2tagSize..ID3::ID3v2tagSize+4] )
    return false if string.size < size
    @raw = string[0...size]    # parse the raw flags:

    if (@rawflags & ID3::TAG_HEADER_FLAG_MASK[@version] != 0)      # in this case we need to skip parsing the frame... and skip to the next one...

      wrong = @rawflags & ID3::TAG_HEADER_FLAG_MASK[@version]
      error = printf "ID3 version %s header flags 0x%X contain invalid flags 0x%X !\n", @version, @rawflags, wrong
      raise ArgumentError, error
    end
    
    @flags = Hash.new
    
    ID3::TAG_HEADER_FLAGS[@version].each{ |key,val|
      # only define the flags which are set..
      @flags[key] = true   if  (@rawflags & val == 1)
    }
  else
    @raw = nil
    @version = nil
    return false
  end  #
  # now parse all the frames
  #

  i = ID3::ID3v2headerSize; # we start parsing right after the ID3v2 header
  
  while (i < @raw.size) && (@raw.getbyte(i) != 0)
    len,frame = parse_frame_header(i)   # this will create the correct frame
    if len != 0
      i += len
    else
      break
    end
  end
  
  has_tag
end

#read_from_file(filename) ⇒ Object Also known as: read


83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/id3/tag2.rb', line 83

def read_from_file(filename)
  f = File.open(filename, 'rb:BINARY')
  has_tag = (f.read(3) == "ID3")
  if has_tag
    major = f.get_byte
    minor = f.get_byte
    @version = "2." + major.to_s + '.' + minor.to_s
    @rawflags = f.get_byte
    size = ID3::ID3v2headerSize + unmungeSize(f.read(4))  # was read_bytes, which was a BUG!!
    f.seek(0)
    @raw = f.read(size) 
    
    # parse the raw flags:
    if (@rawflags & ID3::TAG_HEADER_FLAG_MASK[@version] != 0)      # in this case we need to skip parsing the frame... and skip to the next one...

      wrong = @rawflags & ID3::TAG_HEADER_FLAG_MASK[@version]
      error = printf "ID3 version %s header flags 0x%X contain invalid flags 0x%X !\n", @version, @rawflags, wrong
      raise ArgumentError, error
    end
    
    @flags = Hash.new
    
    ID3::TAG_HEADER_FLAGS[@version].each{ |key,val|
      # only define the flags which are set..
      @flags[key] = true   if  (@rawflags & val == 1)
    }
  else
    @raw = nil
    @version = nil
    return false
  end
  f.close  #
  # now parse all the frames
  #

  i = ID3::ID3v2headerSize; # we start parsing right after the ID3v2 header
  
  while (i < @raw.size) && (@raw.getbyte(i) != 0)
    len,frame = parse_frame_header(i)   # this will create the correct frame
    if len != 0
      i += len
    else
      break
    end
  end
  has_tag
end