Class: ID3Lib::Tag

Inherits:
Array
  • Object
show all
Includes:
Accessors
Defined in:
lib/id3lib.rb

Overview

This class is the main frontend of the library. Use it to read and write ID3 tag data of files.

Example of use

tag = ID3Lib::Tag.read('shy_boy.mp3')

# Remove comments
tag.delete_if{ |frame| frame[:id] == :COMM }

# Set year
tag.year   #=> 2000
tag.year = 2005

# Apply changes
tag.update!

Working with tags

You can use a ID3Lib::Tag object like an array. In fact, it is a subclass of Array. An ID3Lib::Tag contains frames which are stored as hashes, with field IDs as keys and field values as values. The frame IDs like TIT2 are the ones specified by the ID3 standard. If you don’t know these IDs, you probably want to use the accessor methods described afterwards, which have a more natural naming.

tag.each do |frame|
  p frame
end
#=> {:id => :TIT2, :text => "Shy Boy", :textenc => 0}
#=> {:id => :TPE1, :text => "Katie Melua", :textenc => 0}
#=> {:id => :TALB, :text => "Piece By Piece", :textenc => 0}
#=> {:id => :TRCK, :text => "1/12", :textenc => 0}
#=> {:id => :TYER, :text => "2005", :textenc => 0}
#=> {:id => :TCON, :text => "Jazz/Blues", :textenc => 0}

Get and set frames

There are a number of accessors for text frames like title, performer, album, track, year, comment and genre. Have a look at ID3Lib::Accessors for a complete list.

tag.title    #=> "Shy Boi"

tag.title = 'Shy Boy'
tag.title    #=> "Shy Boy"

tag.track    #=> [1,12]
tag.year     #=> 2005

You can always read and write the raw text if you want. You just have to use the “manual access”. It is generally encouraged to use the #frame_text method where possible, because the other two result in an exception when the frame isn’t found.

tag.frame_text(:TRCK)                  #=> "1/12"
tag.frame_text(:TLAN)                  #=> nil

tag.frame(:TRCK)[:text]                #=> "1/12"
# Raises an exception, because nil[:text] isn't possible:
tag.frame(:TLAN)[:text]

tag.find{ |f| f[:id] == :TRCK }[:text] #=> "1/12"
# Also raises an exception:
tag.find{ |f| f[:id] == :TLAN }[:text]

Because only text frames can be set with accessors, you have to add special frames by hand.

# Add two comments
tag << {:id => :COMM, :text => 'chunky bacon'}
tag << {:id => :COMM, :text => 'really.'}

# Add an attached picture
cover = {
  :id          => :APIC,
  :mimetype    => 'image/jpeg',
  :picturetype => 3,
  :description => 'A pretty picture',
  :textenc     => 0,
  :data        => File.read('cover.jpg')
}
tag << cover

Get information about frames

In the last example we added an APIC frame. How can we know what data we have to store in the APIC hash?

ID3Lib::Info.frame(:APIC)[3]
#=> [:textenc, :mimetype, :picturetype, :description, :data]

We see, the last element of the info array obtained through ID3Lib::Info.frame is an array of field IDs needed by APIC.

Have a look at the ID3Lib::Info module for detailed information.

Write changes to file

When you’ve finished modifying a tag, don’t forget to call #update! to write the modifications back to the file. You have to check the return value of update!, it returns nil on failure. This probably means that the file is not writeable or cannot be created.

tag.update!

Getting rid of a tag

Use the #strip! method to completely remove a tag from a file.

tag.strip!

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Accessors

#album, #album=, #band, #band=, #bpm, #bpm=, #comment, #comment=, #comment_frames, #composer, #composer=, #conductor, #conductor=, #date, #date=, #encoded_by, #encoded_by=, #genre, #genre=, #grouping, #grouping=, #interpreted_by, #interpreted_by=, #language, #language=, #lyricist, #lyricist=, #lyrics, #lyrics=, #part_of_set, #part_of_set=, #performer, #performer=, #publisher, #publisher=, #subtitle, #subtitle=, #time, #time=, #title, #title=, #track, #track=, #year, #year=

Constructor Details

#initialize(filename, readtype = V_ALL) ⇒ Tag

Create a new Tag. When a filename is supplied, the tag of the file is read. tagtype specifies the tag type to read and defaults to V_ALL. Use one of ID3Lib::V1, ID3Lib::V2, ID3Lib::V_BOTH or ID3Lib::V_ALL.

tag = ID3Lib::Tag.new('shy_boy.mp3')

Only read ID3v1 tag:

id3v1_tag = ID3Lib::Tag.new('piece_by_piece.mp3', ID3Lib::V1)


161
162
163
164
165
166
167
168
169
# File 'lib/id3lib.rb', line 161

def initialize(filename, readtype=V_ALL)
  @filename = filename
  @readtype = readtype
  @padding = true

  @tag = API::Tag.new
  @tag.link(@filename, @readtype)
  read_frames
end

Instance Attribute Details

#paddingObject

Returns the value of attribute padding.



147
148
149
# File 'lib/id3lib.rb', line 147

def padding
  @padding
end

Instance Method Details

#frame(id) ⇒ Object

Simple shortcut for getting a frame by its id.

tag.frame(:TIT2)
#=> {:id => :TIT2, :text => "Shy Boy", :textenc => 0}

is the same as:

tag.find{ |f| f[:id] == :TIT2 }


189
190
191
# File 'lib/id3lib.rb', line 189

def frame(id)
  find{ |f| f[:id] == id }
end

#frame_text(id) ⇒ Object

Get the text of a frame specified by id. Returns nil if the frame can’t be found.

tag.find{ |f| f[:id] == :TIT2 }[:text]  #=> "Shy Boy"
tag.frame_text(:TIT2)                   #=> "Shy Boy"

tag.find{ |f| f[:id] == :TLAN }         #=> nil
tag.frame_text(:TLAN)                   #=> nil


203
204
205
206
# File 'lib/id3lib.rb', line 203

def frame_text(id)
  f = frame(id)
  f ? f[:text] : nil
end

#has_tag?(type = V2) ⇒ Boolean

Check if there is a tag of type type.

Returns:

  • (Boolean)


281
282
283
284
# File 'lib/id3lib.rb', line 281

def has_tag?(type=V2)
  @tag.link(@filename, V_ALL)
  @tag.has_tag_type(type)
end

#invalid_framesObject

Returns an Array of invalid frames and fields. If a frame ID is invalid, it alone is in the resulting array. If a frame ID is valid but has invalid fields, the frame ID and the invalid field IDs are included.

tag.invalid_frames
#=> [ [:TITS], [:TALB, :invalid] ]


295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
# File 'lib/id3lib.rb', line 295

def invalid_frames
  invalid = []
  each do |frame|
    if not info = Info.frame(frame[:id])
      # Frame ID doesn't exist.
      invalid << [frame[:id]]
      next
    end
    # Frame ID is ok, but are all fields ok?
    invalid_fields = frame.keys.reject { |id|
      info[FIELDS].include?(id) or id == :id
    }
    if not invalid_fields.empty?
      invalid << [frame[:id], *invalid_fields]
    end
  end
  invalid.empty? ? nil : invalid
end

#remove_frame(id) ⇒ Object

Remove all frames with the specified id.



224
225
226
# File 'lib/id3lib.rb', line 224

def remove_frame(id)
  delete_if{ |f| f[:id] == id }
end

#set_frame_text(id, text) ⇒ Object

Set the text of a frame. First, all frames with the specified id are deleted and then a new frame with text is appended.

tag.set_frame_text(:TLAN, 'eng')


214
215
216
217
218
219
# File 'lib/id3lib.rb', line 214

def set_frame_text(id, text)
  remove_frame(id)
  if text
    self << { :id => id, :text => text.to_s }
  end
end

#sizeObject

Returns an estimate of the number of bytes required to store the tag data.



175
176
177
# File 'lib/id3lib.rb', line 175

def size
  @tag.size
end

#strip!(striptype = V_ALL) ⇒ Object

Strip tag from file. This is dangerous because you lose all tag information. Specify striptag to only strip a certain tag type. You don’t have to call #update! after #strip!.

tag.strip!
another_tag.strip!(ID3Lib::V1)


270
271
272
273
274
275
276
# File 'lib/id3lib.rb', line 270

def strip!(striptype=V_ALL)
  clear
  tags = @tag.strip(striptype)
  @tag.clear
  @tag.link(@filename, @readtype)
  tags
end

#update!(writetype = @readtype) ⇒ Object

Updates the tag. This change can’t be undone. writetype specifies which tag type to write and defaults to readtype (see #new).

Invalid frames or frame data is ignored. Use #invalid_frames before update! if you want to know if you have invalid data.

Returns a number corresponding to the written tag type(s) or nil if the update failed.

tag.update!
id3v1_tag.update!(ID3Lib::V1)


241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
# File 'lib/id3lib.rb', line 241

def update!(writetype=@readtype)
  @tag.strip(writetype)
  # The following two lines are necessary because of the weird
  # behaviour of id3lib.
  @tag.clear
  @tag.link(@filename, writetype)

  delete_if do |frame|
    frame_info = Info.frame(frame[:id])
    next true if not frame_info
    libframe = API::Frame.new(frame_info[NUM])
    Frame.write(frame, libframe)
    @tag.add_frame(libframe)
    false
  end

  @tag.set_padding(@padding)
  tags = @tag.update(writetype)
  return tags == 0 ? nil : tags
end