3 function url_decode(str)
4 if not str then return nil end
5 str = string.gsub (str, "+", " ")
6 str = string.gsub (str, "%%(%x%x)", function(h) return
7 string.char(tonumber(h,16)) end)
8 str = string.gsub (str, "\r\n", "\n")
12 function split(s, delimiter)
14 for match in (s..delimiter):gmatch("(.-)"..delimiter) do
15 result[#result+1] = match;
20 function string.starts(String,Start)
21 return string.sub(String,1,string.len(Start))==Start
24 function process_playlist(playlist)
26 for _,record in pairs(playlist) do
27 local splitted = split(record,": ")
28 local name = splitted[2]
29 local splitted = split(splitted[1],":")
30 local id = splitted[1]
31 local rectype = splitted[2]
33 if not (id == "OK") then
43 function process_playlists(playlists)
45 for _,record in pairs(playlists) do
46 local splitted = split(record,": ")
47 if splitted[1]=="playlist" then
48 res[#res+1] = splitted[2]
54 function process_directory(directory)
56 for _,record in pairs(directory) do
57 local splitted = split(record,": ")
58 if splitted[1]=="directory" or splitted[1]=="file" then
60 rec["type"] = splitted[1]
61 rec["name"] = splitted[2]
71 function mpd_new(settings)
73 if settings == nil then settings = {} end
75 client.hostname = settings.hostname or "localhost"
76 client.port = settings.port or 6600
77 client.desc = settings.desc or client.hostname
78 client.password = settings.password
79 client.timeout = settings.timeout or 1
80 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
168 settings['host'] = x.get("mpd","server","host") or "localhost"
169 settings['port'] = x.get("mpd","server","port") or 6600
170 password = x.get("mpd","server","password")
172 settings["password"] = password
175 m = mpd_new(settings)
177 command = url_decode(os.getenv('QUERY_STRING'))
179 if not command or command=="" then
183 if command=="play" or command=="pause" or command=="stop" or command=="previous" or command=="next" then
184 res=mpd_send(m,command)
185 elseif command=="vold" then
186 status=mpd_send(m,"status")
187 volume=tonumber(status["volume"])
188 res=mpd_send(m,"setvol "..(volume-3))
189 elseif command=="volu" then
190 status=mpd_send(m,"status")
191 volume=tonumber(status["volume"])
192 res=mpd_send(m,"setvol "..(volume+3))
193 elseif command=="status" then
194 res=mpd_send(m,"status")
196 playlist=mpd_send(m,"playlist",1)
197 pl=process_playlist(playlist)
199 res['current_playing']=pl[song]['name']
201 res['current_playing']="No songs selected"
203 elseif command=="playlist" then
204 playlist=mpd_send(m,"playlist",1)
205 res=process_playlist(playlist)
206 elseif command=="repeat" then
207 status=mpd_send(m,"status")
208 rep=1-status["repeat"]
209 res=mpd_send(m,"repeat "..rep)
210 elseif string.starts(command,"cpl") then
211 cmd=split(command,"|")
214 if command=="playitem" then
216 res=mpd_send(m,command)
218 if command=="clear" then
219 res=mpd_send(m,"clear")
221 if command=="remove" then
222 command="delete "..id
223 res=mpd_send(m,command)
225 if command=="moveup" then
226 command="swap "..id.." "..(id-1)
227 res=mpd_send(m,command)
229 if command=="movedown" then
230 command="swap "..id.." "..(id+1)
231 res=mpd_send(m,command)
233 elseif string.starts(command,"lists") then
234 cmd=split(command,"|")
236 if command=="load" then
238 lists=mpd_send(m,"listplaylists",1)
239 res=process_playlists(lists)
241 res=mpd_send(m,"load "..cmd[3],1)
244 if command=="save" and cmd[3] then
245 res=mpd_send(m,"save "..cmd[3],1)
247 if command=="delete" and cmd[3] then
248 res=mpd_send(m,"rm "..cmd[3],1)
250 if command=="edit" then
252 dir=mpd_send(m,"lsinfo \""..cmd[3].."\"",1)
254 dir=mpd_send(m,"lsinfo",1)
256 res=process_directory(dir)
258 if command=="add" then
261 res=mpd_send(m,"add \""..cmd[3].."\"")
267 print("Content-Type: text/plain\r\n")
268 print("MPD server - unknown command "..command)
269 elseif (res['error_msg']) then
270 print("Content-Type: text/plain\r\n")
271 print("MPD server connection error: "..res['error_msg'])
273 print "Content-Type: text/plain\r\n"
274 print(json.encode(res))