class Thingfish::Processor::MP3

Attach ID3 data to an mp3, along with any embedded album art as a related resource.

Constants

APIC_FORMAT

Attached picture “APIC”

Text encoding   $xx
MIME type       <text string> $00
Picture type    $xx
Description     <text string according to encoding> $00 (00)
Picture data    <binary data>
NULL

Null character

PIC_FORMAT

Attached picture “PIC”

Frame size         $xx xx xx
---- mp3info is 'helpfully' cropping out frame size.
Text encoding      $xx
Image format       $xx xx xx
Picture type       $xx
Description        <textstring> $00 (00)
Picture data       <binary data>
REVISION

Version control revision

VERSION

Package version

Public Instance Methods

extract_id3_metadata( mp3info ) click to toggle source

Normalize metadata from the MP3Info object and return it as a hash.

# File lib/thingfish/processor/mp3.rb, line 66
def extract_id3_metadata( mp3info )
        self.log.debug "Extracting MP3 metadata"

        mp3_metadata = {
                'mp3:frequency' => mp3info.samplerate,
                'mp3:bitrate'   => mp3info.bitrate,
                'mp3:vbr'       => mp3info.vbr,
                'mp3:title'     => mp3info.tag.title,
                'mp3:artist'    => mp3info.tag.artist,
                'mp3:album'     => mp3info.tag.album,
                'mp3:year'      => mp3info.tag.year,
                'mp3:genre'     => mp3info.tag.genre,
                'mp3:tracknum'  => mp3info.tag.tracknum,
                'mp3:comments'  => mp3info.tag.comments,
        }

        # ID3V2 2.2.0 has three-letter tags, so map those if the artist info isn't set
        if mp3info.hastag2?
                if mp3_metadata['mp3:artist'].nil?
                        self.log.debug "   extracting old-style ID3v2 info" % [mp3info.tag2.version]

                        mp3_metadata.merge!({
                                'mp3:title'     => mp3info.tag2.TT2,
                                'mp3:artist'    => mp3info.tag2.TP1,
                                'mp3:album'     => mp3info.tag2.TAL,
                                'mp3:year'      => mp3info.tag2.TYE,
                                'mp3:tracknum'  => mp3info.tag2.TRK,
                                'mp3:comments'  => mp3info.tag2.COM,
                                'mp3:genre'     => mp3info.tag2.TCO,
                        })
                end
        end

        self.log.debug "  raw metadata: %p" % [ mp3_metadata ]
        return sanitize_values( mp3_metadata )
end
extract_images( mp3info ) { |string_io, :format => mime, :extent => length, :relationship => 'album-art'| ... } click to toggle source

Extract image data from ID3 information, supports both APIC (2.3) and the older style PIC (2.2). Return value is a hash with IO keys and mimetype values. {

io  => { format => 'image/jpeg' }
io2 => { format => 'image/jpeg' }

}

# File lib/thingfish/processor/mp3.rb, line 110
def extract_images( mp3info )
        self.log.debug "Extracting embedded images"
        raise LocalJumpError, "no block given" unless block_given?

        unless mp3info.hastag2?
                self.log.debug "...no id3v2 tag, so no embedded images possible."
                return
        end

        self.log.debug "...id3v2 tag present..."

        if mp3info.tag2.APIC
                self.log.debug "...extracting APIC (id3v2.3+) image data."

                images = [ mp3info.tag2.APIC ].flatten
                images.each do |img|
                        blob, mime = img.unpack( APIC_FORMAT ).values_at( 4, 1 )
                        yield( StringIO.new(blob),
                                :format       => mime,
                                :extent       => blob.length,
                                :relationship => 'album-art' )
                end

        elsif mp3info.tag2.PIC
                self.log.debug "...extracting PIC (id3v2.2) image data."

                images = [ mp3info.tag2.PIC ].flatten
                images.each do |img|
                        blob, type = img.unpack( PIC_FORMAT ).values_at( 4, 1 )
                        mime = Mongrel2::Config.mimetypes[ ".#{type.downcase}" ] or next
                        yield( StringIO.new(blob),
                                :format       => mime,
                                :extent       => blob.length,
                                :relationship => 'album-art' )
                end

        else
                self.log.debug "...no known image tag types in tags: %p" % [ mp3info.tag2.keys.sort ]
        end
end
on_request( request ) click to toggle source

Synchronous processor API – extract metadata from uploaded MP3s

# File lib/thingfish/processor/mp3.rb, line 52
def on_request( request )
        mp3info = Mp3Info.new( request.body )

        mp3_metadata = self.extract_id3_metadata( mp3info )
        request.add_metadata( mp3_metadata )

        self.extract_images( mp3info ) do |imageio, metadata|
                metadata[:title] = "Album art for %s - %s" % mp3_metadata.values_at( 'mp3:artist', 'mp3:title' )
                request.add_related_resource( imageio, metadata )
        end
end

Private Instance Methods

sanitize_values( metadata_hash ) click to toggle source

Strip NULLs from the values of the given metadata_hash and return it.

# File lib/thingfish/processor/mp3.rb, line 158
def sanitize_values( metadata_hash )
        metadata_hash.each do |k,v|
                case v
                when String
                        metadata_hash[k] = v.chomp(NULL).strip
                when Array
                        metadata_hash[k] = v.collect {|vv| vv.chomp(NULL).strip }
                when Numeric, TrueClass, FalseClass
                        # No-op
                end
        end

        return metadata_hash.delete_if {|_,v| v.nil? }
end