# Last.fm Tagger v0.7 # # Author: # wes@633k.net # # Abstract: # For each selected track in iTunes, retrieve the genre from Last.fm and accordingly tag the track. # Use -q to subdue confirmations # # Todo: # Option to tag artist if no tag exists in last.fm records # Retrieve all tags above 50, prompt for tag choice begin require 'rubygems'; rescue LoadError; end %W{rbosa cgi rexml/document}.map { |i| require i } OSA.utf8_strings = true class Artist attr_reader :name, :genre def initialize(name='', genre='') @name, @genre = name, genre query unless name == '' end def query puts %{Querying last.fm for: "#{@name}"...} begin doc = REXML::Document.new(Net::HTTP.get(URI("http://ws.audioscrobbler.com/1.0/artist/#{URI.escape(CGI.escape(@name))}/toptags.xml"))) rescue EOFError => e puts "#{Color.error} Request timed out! Trying again..." query end genre = REXML::XPath.first(doc, "//toptags/tag/name") genre.nil? ? @genre = "" : @genre = genre.text.strip @name = name.strip end end class LastfmTagger @@tagged, @@skipped = 0, 0 def initialize itunes = OSA.app('iTunes') @selection = itunes.selection.get if @selection.empty? $stderr.puts "#{Color.error} Please select some tracks in iTunes" else # construct pseudo Artist to satify @selection loop @artist = Artist.new end end def process @selection.each do |track| # check if artist differs; if so, reinstantiate @artist if @artist.name.downcase != track.artist.strip.downcase @artist = Artist.new(track.artist) # no need to write tag if it is identical, therefore skip until it differs if track.genre.strip.downcase == @artist.genre.downcase puts "#{Color.pass} Identical genres. Skipping..." @@skipped += 1 @input = 'n' elsif @artist.genre == "" puts %{#{Color.pass} No tags found for "#{@artist.name}".} @@skipped += 1 @input = 'n' else @input = confirm end end track.genre = @artist.genre if @input == 'y' end puts "Done. Tagged #{@@tagged} artists. Skipped #{@@skipped}." end def confirm print %{#{Color.success} Tagging "#{@artist.name}" as "#{@artist.genre}". } @@tagged += 1 # add some space when in quiet mode if quiet? puts return 'y' end print %{Continue? (Y/N) } gets.chomp.downcase end # handles -q argument def quiet? return true if ARGV[0] == '-q' end end class Color def self.pass "[ \e[33mPASS\e[0m ]" end def self.success "[ \e[32mSUCCESS\e[0m ]" end def self.error "[ \e[31mERROR\e[0m ]" end end LastfmTagger.new.process