7 function url_decode(str)
 
   8   if not str then return nil end
 
   9   str = string.gsub (str, "+", " ")
 
  10   str = string.gsub (str, "%%(%x%x)", function(h) return
 
  11     string.char(tonumber(h,16)) end)
 
  12   str = string.gsub (str, "\r\n", "\n")
 
  16 function split(s, delimiter)
 
  18     for match in (s..delimiter):gmatch("(.-)"..delimiter) do
 
  19         result[#result+1] = match;
 
  24 function string.starts(String,Start)
 
  25    return string.sub(String,1,string.len(Start))==Start
 
  28 function process_playlist(playlist)
 
  30   for _,record in pairs(playlist) do
 
  31     local splitted = split(record,": ")
 
  32     local name = splitted[2]
 
  33     local splitted = split(splitted[1],":")
 
  34     local id = splitted[1]
 
  35     local rectype = splitted[2]
 
  37     if not (id == "OK") then
 
  47 function process_playlists(playlists)
 
  49   for _,record in pairs(playlists) do
 
  50     local splitted = split(record,": ")
 
  51     if splitted[1]=="playlist" then
 
  52       res[#res+1] = splitted[2]
 
  58 function process_directory(directory)
 
  60   for _,record in pairs(directory) do
 
  61     local splitted = split(record,": ")
 
  62     if splitted[1]=="directory" or splitted[1]=="file" then
 
  64       rec["type"] = splitted[1]
 
  65       rec["name"] = splitted[2]
 
  72 function mpd_new(settings)
 
  74     if settings == nil then settings = {} end
 
  76     client.hostname = settings.hostname or "localhost"
 
  77     client.port     = settings.port or 6600
 
  78     client.desc     = settings.desc or client.hostname
 
  79     client.password = settings.password
 
  80     client.timeout  = settings.timeout or 1
 
  81     client.retry    = settings.retry or 60
 
  86 function mpd_send(mpd,action,raw)
 
  88     local command = string.format("%s\n", action)
 
  91     -- connect to MPD server if not already done.
 
  92     if not mpd.connected then
 
  93         local now = os.time();
 
  94         if not mpd.last_try or (now - mpd.last_try) > mpd.retry then
 
  95             mpd.socket = socket.tcp()
 
  96             mpd.socket:settimeout(mpd.timeout, 't')
 
  97             mpd.last_try = os.time()
 
  98             mpd.connected = mpd.socket:connect(mpd.hostname, mpd.port)
 
  99             if not mpd.connected then
 
 100                 return { errormsg = "could not connect" }
 
 104             -- Read the server's hello message
 
 105             local line = mpd.socket:receive("*l")
 
 106             if not line:match("^OK MPD") then -- Invalid hello message?
 
 107                 mpd.connected = false
 
 108                 return { errormsg = string.format("invalid hello message: %s", line) }
 
 110                 _, _, mpd.version = string.find(line, "^OK MPD ([0-9.]+)")
 
 113             -- send the password if needed
 
 115                 local rsp = mpd_send(mpd,string.format("password %s", mpd.password))
 
 121             local retry_sec = mpd.retry - (now - mpd.last_try)
 
 122             return { errormsg = string.format("%s (retrying in %d sec)", mpd.last_error, retry_sec) }
 
 126     mpd.socket:send(command)
 
 128     local line = ""; err=0
 
 129     while not line:match("^OK$") do
 
 130         line, err = mpd.socket:receive("*l")
 
 131         if not line then -- closed,timeout (mpd killed?)
 
 133             mpd.connected = false
 
 135             return mpd_send(mpd,action)
 
 138         if line:match("^ACK") then
 
 139             return { errormsg = line }
 
 144           local pattern = string.format("(%s)", ": ")
 
 145           local i = string.find (line, pattern, 0)
 
 148               local key=string.sub(line,1,i-1)
 
 149               local value=string.sub(line,i+2,-1)
 
 150               values[string.lower(key)] = value
 
 155           values[#values+1]=line
 
 166 settings['host'] = x.get("mpd","server","host") or "localhost"
 
 167 settings['port'] = x.get("mpd","server","port") or 6600
 
 168 settings['timeout'] = x.get("mpd","server","timeout") or 1
 
 170 volstep = x.get("mpd","control","volume_step") or 3
 
 172 password = x.get("mpd","server","password")
 
 174   settings["password"] = password
 
 177 m = mpd_new(settings)
 
 179 command = url_decode(os.getenv('QUERY_STRING'))
 
 181 if not command or command=="" then
 
 185 if command=="play" or command=="pause" or command=="stop" or command=="previous" or command=="next" then
 
 187   res=mpd_send(m,command)
 
 189 elseif command=="idle" then
 
 192   res=mpd_send(m,command)
 
 194 elseif command=="vold" then
 
 196   status=mpd_send(m,"status")
 
 197   volume=tonumber(status["volume"])
 
 198   res=mpd_send(m,"setvol "..(volume-volstep))
 
 200 elseif command=="volu" then
 
 202   status=mpd_send(m,"status")
 
 203   volume=tonumber(status["volume"])
 
 204   res=mpd_send(m,"setvol "..(volume+volstep))
 
 206 elseif command=="status" then
 
 208   res=mpd_send(m,"status")
 
 210   playlist=mpd_send(m,"playlist",1)
 
 211   pl=process_playlist(playlist)
 
 214     res['current_playing']=pl[song]['name']
 
 216     res['current_playing']="No songs selected"
 
 219 elseif command=="playlist" then
 
 221   playlist=mpd_send(m,"playlist",1)
 
 222   res=process_playlist(playlist)
 
 224 elseif command=="repeat" then
 
 226   status=mpd_send(m,"status")
 
 227   rep=1-status["repeat"]
 
 228   res=mpd_send(m,"repeat "..rep)
 
 230 elseif string.starts(command,"cpl") then
 
 232   cmd=split(command,"|")
 
 236   if command=="playitem" then
 
 238     res=mpd_send(m,command)
 
 241   if command=="clear" then
 
 242     res=mpd_send(m,"clear")
 
 245   if command=="remove" then
 
 246     command="delete "..id
 
 247     res=mpd_send(m,command)
 
 250   if command=="moveup" then
 
 251     command="swap "..id.." "..(id-1)
 
 252     res=mpd_send(m,command)
 
 255   if command=="movedown" then
 
 256     command="swap "..id.." "..(id+1)
 
 257     res=mpd_send(m,command)
 
 260 elseif string.starts(command,"lists") then
 
 262   cmd=split(command,"|")
 
 265   if command=="load" then
 
 267       lists=mpd_send(m,"listplaylists",1)
 
 268       res=process_playlists(lists)
 
 270       res=mpd_send(m,"load "..cmd[3],1)
 
 274   if command=="save" and cmd[3] then
 
 275     res=mpd_send(m,"save "..cmd[3],1)
 
 278   if command=="delete" and cmd[3] then
 
 279     res=mpd_send(m,"rm "..cmd[3],1)
 
 282   if command=="edit" then
 
 284       dir=mpd_send(m,"lsinfo \""..cmd[3].."\"",1)
 
 286       dir=mpd_send(m,"lsinfo",1)
 
 288     res=process_directory(dir)
 
 291   if command=="add" then
 
 294       res=mpd_send(m,"add \""..cmd[3].."\"")
 
 301   print("Content-Type: text/plain\r\n")
 
 302   print("MPD server - unknown command "..command)
 
 303 elseif (res['error_msg']) then
 
 304   print("Content-Type: text/plain\r\n")
 
 305   print("MPD server connection error: "..res['error_msg'])
 
 307   print "Content-Type: text/plain\r\n"
 
 308   print(json.encode(res))