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))