From 2ef760f166525df2dd7c7cfb6609d6aec8fdac2a Mon Sep 17 00:00:00 2001 From: rjp Date: Fri, 18 May 2012 13:55:02 +0100 Subject: [PATCH 1/2] Minor tidyups --- despotway.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/despotway.rb b/despotway.rb index d093584..5f38b2e 100644 --- a/despotway.rb +++ b/despotway.rb @@ -5,6 +5,8 @@ require 'xspf' require 'uri' +### id2uri ## cargo-culted from lib/despotify.c + EncodeAlphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/"; EncodeHash = {} EncodeAlphabet.split('').each_with_index {|l,i| EncodeHash[l] = i} @@ -48,6 +50,7 @@ def id2uri(id) return out.strip end +### Despot ## our interface to despotify-gateway class Despot attr_accessor :username, :password, :host, :port @@ -202,7 +205,7 @@ def load_track(tid) if tid =~ /^spotify:local/ then # we can't look this track up remotely s, l, artist, album, title, duration = tid.split(/:/).map{|i| URI.unescape(i.gsub(/\+/,' '))} - track = {:title => title, :artist => artist, :album => album, :tid => tid, :uri => tid, :duration = 1000*duration.to_i} + track = {:title => title, :artist => artist, :album => album, :tid => tid, :uri => tid, :duration => 1000*duration.to_i} @track_cache[tid] = track return track end From 4396f6d2daf6a2214e8032b8d108269cc263725f Mon Sep 17 00:00:00 2001 From: rjp Date: Fri, 18 May 2012 14:47:08 +0100 Subject: [PATCH 2/2] Retrieve the UPC using the Spotify HTTP Metadata API for albums If we find a UPC, smoosh it into the XSPF Track Identifier (because the Ruby XSPF library is broken) Cache album metadata per session to avoid abuse TODO stick the returned XML into an SDBM store for intra-session caching --- despotway.rb | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/despotway.rb b/despotway.rb index 5f38b2e..94e39ce 100644 --- a/despotway.rb +++ b/despotway.rb @@ -4,6 +4,7 @@ require 'socket' require 'xspf' require 'uri' +require 'open-uri' ### id2uri ## cargo-culted from lib/despotify.c @@ -54,7 +55,7 @@ def id2uri(id) class Despot attr_accessor :username, :password, :host, :port - attr_accessor :track_cache, :outputdir + attr_accessor :track_cache, :outputdir, :metacache, :metafile attr_accessor :socket, :playlists, :tracks def initialize(username, password, host, port, outputdir) @@ -63,12 +64,52 @@ def initialize(username, password, host, port, outputdir) @username = username @password = password @outputdir = outputdir + @metacache = {} # in-memory transient cache for now @socket = TCPSocket.new(host, port) @playlists = [] @tracks = {} @track_cache = {} end + def init_metacache + end + + def album_metadata(aid) + if not @metacache[aid].nil? then + return @metacache[aid] + end + + # not cached, we need to look it up from the web API + aid_uri = id2uri(aid) + $stderr.puts "W #{aid} #{aid_uri}" + uri = "http://ws.spotify.com/lookup/1/?uri=spotify:album:#{aid_uri}" + xml_meta = open(uri).read + $stderr.puts xml_meta + aid_meta = {} + aid_meta[:id] = Hash.new() + if not xml_meta.nil? then + xml_dom = Nokogiri::XML(xml_meta) + + if xml_dom.nil? then + return nil + end + + # I hate having to do this + xml_dom.remove_namespaces! + + # collect all the album metadata bits here + xml_dom.search("/album/id").each do |idnode| + id_type = idnode['type'] + aid_meta[:id][id_type] = idnode.inner_text.strip + end + else + return nil + end + + @metacache[aid] = aid_meta + return aid_meta + end + def write_playlist(playlist) begin Dir.mkdir(@outputdir) @@ -99,6 +140,19 @@ def write_playlist(playlist) if not track[:index].nil? then t.tracknum = track[:index].to_s end + # do we have any album metadata? + if not track[:album_meta].nil? then + # we always have a :id subkey by design + # FIXME extend this to check for keys.size>0 and then smoosh upc into identifier with a grep + upc = track[:album_meta][:id]['upc'] + + # we have a UPC, smoosh it into the identifier if we have one + if not upc.nil? then + old = t.identifier + t.identifier = [old, "upc:#{upc}"].join(' ') + end + end + tl << t end @@ -222,12 +276,15 @@ def load_track(tid) title = dom.at("//track/title").inner_text.strip artist = dom.at("//track/artist").inner_text.strip album = dom.at("//track/album").inner_text.strip + album_id = dom.at("//track/album-id").inner_text.strip index = dom.at("//track/track-number").inner_text.strip duration = dom.at("//track/length").inner_text.strip uri = id2uri(tid) - track = {:title => title, :artist => artist, :album => album, :tid => tid, :uri => uri, :index => index, :duration => duration} + almeta = self.album_metadata(album_id) + + track = {:title => title, :artist => artist, :album => album, :tid => tid, :uri => uri, :index => index, :duration => duration, :aid => album_id, :album_meta => almeta} eid = dom.at("//track/external-ids/external-id") if not eid.nil? and eid['type'] == 'isrc' then