class SubsonicAPI

Constants

REQUIRED_SETTINGS

Public Class Methods

new(configs) click to toggle source
# File lib/subcl/subsonic_api.rb, line 16
def initialize(configs)
  @configs = {
    :appname => 'subcl',
    :app_version => '0.0.4',
    :proto_version => '1.9.0', #subsonic API protocol version
    :max_search_results => 20,
    :random_song_count => 10
  }.merge! configs.to_hash

  REQUIRED_SETTINGS.each do |setting|
    unless @configs.key? setting
      raise "Missing setting '#{setting}'"
    end
  end
end

Public Instance Methods

add_basic_auth(uri) click to toggle source

adds the basic auth parameters from the config to the URI

# File lib/subcl/subsonic_api.rb, line 222
def add_basic_auth(uri)
  uri.user = @configs[:username]
  uri.password = @configs[:password]
  return uri
end
album_songs(id) click to toggle source

returns an array of songs for the given album id

# File lib/subcl/subsonic_api.rb, line 51
def album_songs(id)
  doc = query('getAlbum.view', {:id => id})
  doc.elements.collect('subsonic-response/album/song') do |song|
    decorate_song(song.attributes)
  end
end
albumart_url(streamUrl, size = nil) click to toggle source

returns the albumart URL for the song

# File lib/subcl/subsonic_api.rb, line 236
def albumart_url(streamUrl, size = nil)
  raise ArgumentError if streamUrl.empty?
  id = CGI.parse(URI.parse(streamUrl).query)['id'][0]
  params = {:id => id};
  params[:size] = size unless size.nil?
  add_basic_auth(
    build_url('getCoverArt.view', params)
  )
end
albumlist(type = :random) click to toggle source

returns a list of albums from the specified type www.subsonic.org/pages/api.jsp#getAlbumList2

# File lib/subcl/subsonic_api.rb, line 101
def albumlist(type = :random)
  #TODO might want to add validation for the type here
  doc = query('getAlbumList2.view', {:type => type})
  doc.elements.collect('subsonic-response/albumList2/album') do |album|
    album = album.attributes
    album = Hash[album.collect { |key,val| [key.to_sym, val] }]
    album[:type] = :album
    album
  end
end
all_playlists() click to toggle source

returns all playlists

# File lib/subcl/subsonic_api.rb, line 75
def all_playlists
  doc = query('getPlaylists.view')
  doc.elements.collect('subsonic-response/playlists/playlist') do |playlist|
    {
      :id => playlist.attributes['id'],
      :name => playlist.attributes['name'],
      :owner => playlist.attributes['owner'],
      :type => :playlist
    }
  end
end
artist_songs(id) click to toggle source

returns an array of songs for the given artist id

# File lib/subcl/subsonic_api.rb, line 59
def artist_songs(id)
  doc = query('getArtist.view', {:id => id})
  doc.elements.inject('subsonic-response/artist/album', []) do |memo, album|
    memo += album_songs(album.attributes['id'])
  end
end
build_url(method, params) click to toggle source
# File lib/subcl/subsonic_api.rb, line 213
def build_url(method, params)
  params[:v] = @configs[:proto_version]
  params[:c] = @configs[:appname]
  query = params.collect {|k,v| "#{k}=#{URI.escape(v.to_s)}"}.join('&')

  URI("#{@configs[:server]}/rest/#{method}?#{query}")
end
decorate_song(attributes) click to toggle source

takes the attributes of a song tag from the xml and applies the :type and :stream_url attribute

# File lib/subcl/subsonic_api.rb, line 124
def decorate_song(attributes)
  attributes = Hash[attributes.collect {|key, val| [key.to_sym, val]}]
  attributes[:type] = :song
  attributes[:stream_url] = stream_url(attributes[:id])
  attributes
end
get_playlists(name) click to toggle source

returns all playlists matching name subsonic features no mechanism to search by playlist name, so this method retrieves all playlists and and filters them locally. This might become problematic when the server has a huge amount of playlists

# File lib/subcl/subsonic_api.rb, line 92
def get_playlists(name)
  name.downcase!
  all_playlists().select do |playlist|
    playlist[:name].downcase.include? name
  end
end
get_songs(entities) click to toggle source

takes a list of albums or artists and returns a list of their songs

# File lib/subcl/subsonic_api.rb, line 33
def get_songs(entities)
  entities.collect_concat do |entity|
    case entity[:type]
    when :song
      entity
    when :album
      album_songs(entity[:id])
    when :artist
      artist_songs(entity[:id])
    when :playlist
      playlist_songs(entity[:id])
    else
      raise "Cannot get songs for '#{entity[:type]}'"
    end
  end
end
playlist_songs(id) click to toggle source

returns all songs from playlist(s) matching the name

# File lib/subcl/subsonic_api.rb, line 67
def playlist_songs(id)
  doc = query('getPlaylist.view', {:id => id})
  doc.elements.collect('subsonic-response/playlist/entry') do |entry|
    decorate_song(entry.attributes)
  end
end
query(method, params = {}) click to toggle source
# File lib/subcl/subsonic_api.rb, line 179
def query(method, params = {})
  uri = build_url(method, params)
  LOGGER.debug { "query: #{uri} (basic auth sent per HTTP header)" }

  req = Net::HTTP::Get.new(uri.request_uri)
  req.basic_auth(@configs[:username], @configs[:password])
  res = Net::HTTP.start(uri.hostname, uri.port) do |http|
    http.request(req)
  end

  doc = Document.new(res.body)

  LOGGER.debug { "response: " + doc.to_s }

  #handle error response
  doc.elements.each('subsonic-response/error') do |error|
    raise SubclError, "#{error.attributes["message"]} (#{error.attributes["code"]})"
  end

  #handle http error
  case res.code
  when '200'
    return doc
  else
    msg = case res.code
    when '401'
      "HTTP 401. Might be an incorrect username/password"
    else
      "HTTP #{res.code}"
    end
    raise SubclError, msg
  end
end
random_songs(count = @configs[:random_song_count]) click to toggle source
# File lib/subcl/subsonic_api.rb, line 112
def random_songs(count = @configs[:random_song_count])
  #throws an exception if its not parseable to an int
  count = Integer(count)

  doc = query('getRandomSongs.view', {:size => count})
  doc.elements.collect('subsonic-response/randomSongs/song') do |song|
    decorate_song(song.attributes)
  end
end
stream_url(songid) click to toggle source

returns the streaming URL for the song, including basic auth

# File lib/subcl/subsonic_api.rb, line 229
def stream_url(songid)
  raise ArgumentError, "no songid!" unless songid
  uri = build_url('stream.view', {:id => songid})
  add_basic_auth(uri)
end