Last.fm cover grabber

Instruction how to scan mp3 file (id3tag v1) for artist and album and based on this info save cover art using Last.fm API.

First of all create account on the Last.fm api page: Last.fm API to receive user and api key.

Use received user and key with the following script:

set api_key "some api key"
set user "some user"

package require http
package require tdom

#procedure to read metadata from id3tag
proc ___readMetadata {fn} {
        catch {set fh [open $fn r]} status
        if {[string match "*couldn't open*" $status]} {
                return -1
        }
        fconfigure $fh -encoding binary -translation binary
        seek $fh -128 end
        set tag [read $fh]
        close $fh
        binary scan $tag A3 id
        if {$id eq "TAG"} {
                binary scan $tag A3A30A30A30A4A28ccc id title artist album  year comment zero track genre
                return [list $title $artist $album $year $comment]
        } else {
                return -1
        }
}

#last.fm api
#artist.getInfo
proc ___artistGetInfo {artist} {
        global api_key
        set method "artist.getInfo"
        set query [::http::formatQuery method $method api_key $api_key artist $artist]
        return [___requestData $query]
}

#album.getinfo
proc ___albumGetInfo {artist album} {
        global api_key
        global user
        set method "album.getinfo"
        set query [::http::formatQuery method $method api_key $api_key artist $artist album $album user $user]
        return [___requestData $query]
}

#run query and return received data
proc ___requestData {query} {
        set r [::http::geturl http://ws.audioscrobbler.com/2.0/ -query $query]
        set data [::http::data $r]
        ::http::cleanup $r
        return $data        
}

#parse received xml
proc ___parseXML {xml data} {
        global xmlStruct
        set dom [dom parse $xml]
        set doc [$dom documentElement]
        foreach n $data {
                catch {set path $xmlStruct($n)} status
                if {[string match "*can't read*" $status]} {
                        puts "Element $n is not defined"
                        continue
                }
                set node [$doc selectNodes $path]
                if {[string length $node] > 0} {
                        lappend res [list $n [string trimleft [$node text]]]
                } else {
                        lappend res [list $n ""]
                }
        }
        return $res
}

#save picture under name for entered ulr
proc ___savePicture {name url path} {
        catch {set fh [open [file join $path $name] w]} status
        if {[string match "*couldn't open*" $status]} {
                return -1
        }        
        fconfigure $fh -translation binary
        set imgtok [http::geturl $url -binary true -channel $fh]
        set Stat [::http::status $imgtok]
        flush $fh
        close $fh
        http::cleanup $imgtok
}



#define position of name artist and release date in the xml
set xmlStruct(album) "/lfm/album/name"
set xmlStruct(artist) "/lfm/album/artist"
set xmlStruct(releasedate) "/lfm/album/releasedate"
set xmlStruct(picture) {/lfm/album/image[@size='large']}

#example how to check album info, extract cover url and save it
proc ___getMetadata {artist album {path ""}} {
        set ret [___parseXML [___albumGetInfo $artist $album] {album artist releasedate picture}]
        foreach n $ret {
                puts $n
                switch [lindex $n 0] {
                        "picture" {
                                if {[string length [lindex $n 1]] > 5} {
                                        set nName [file split [lindex $n 1]]
                                        set nName [lindex $nName [expr [llength $nName] -1]]
                                        ___savePicture $nName [lindex $n 1] $path
                                        return $nName
                                }
                        }
                }
        }
}

#scan file, check metainfo via last.fm and save cove art
proc prepareCover {fileN path} {
        lassign [___readMetadata [file join $path $fileN]] title artist album year comment
        set pName [___getMetadata $artist $album]
}

To start script call prepareCover procedure:

prepareCover 02_test.mp3 ./