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" then
187 res=mpd_send(m,command)
189 elseif command=="previous" or command=="next" then
191 res=mpd_send(m,"play")
192 res=mpd_send(m,command)
194 elseif command=="idle" then
197 res=mpd_send(m,command)
199 elseif command=="vold" then
201 status=mpd_send(m,"status")
202 volume=tonumber(status["volume"])
203 res=mpd_send(m,"setvol "..(volume-volstep))
205 elseif command=="volu" then
207 status=mpd_send(m,"status")
208 volume=tonumber(status["volume"])
209 res=mpd_send(m,"setvol "..(volume+volstep))
211 elseif string.starts(command,"fastfwd") then
213 cmd=split(command,"|")
214 skip=tonumber(cmd[2])
219 status=mpd_send(m,"status")
220 rec_time=status["time"]
226 rec_time=split(rec_time,":")
227 cur_time=tonumber(rec_time[1])
229 track_time=tonumber(rec_time[2])
230 cur_time=cur_time+skip
231 if cur_time>track_time then
235 mpd_send(m,"seek "..song.." "..cur_time)
247 elseif string.starts(command,"rewind") then
249 cmd=split(command,"|")
250 skip=tonumber(cmd[2])
255 status=mpd_send(m,"status")
256 rec_time=status["time"]
262 rec_time=split(rec_time,":")
263 cur_time=tonumber(rec_time[1])
265 track_time=tonumber(rec_time[2])
266 cur_time=cur_time-skip
271 mpd_send(m,"seek "..song.." "..cur_time)
276 mpd_send(m,"previous")
284 elseif command=="status" then
286 res=mpd_send(m,"status")
288 playlist=mpd_send(m,"playlist",1)
289 pl=process_playlist(playlist)
292 res['current_playing']=pl[song]['name']
295 res['current_playing']="---"
298 elseif command=="playlist" then
300 playlist=mpd_send(m,"playlist",1)
301 res=process_playlist(playlist)
303 elseif command=="repeat" then
305 status=mpd_send(m,"status")
306 rep=1-status["repeat"]
307 res=mpd_send(m,"repeat "..rep)
309 elseif string.starts(command,"cpl") then
311 cmd=split(command,"|")
315 if command=="playitem" then
317 res=mpd_send(m,command)
320 if command=="clear" then
321 res=mpd_send(m,"clear")
324 if command=="remove" then
325 command="delete "..id
326 res=mpd_send(m,command)
329 if command=="moveup" then
330 command="swap "..id.." "..(id-1)
331 res=mpd_send(m,command)
334 if command=="movedown" then
335 command="swap "..id.." "..(id+1)
336 res=mpd_send(m,command)
339 elseif string.starts(command,"lists") then
341 cmd=split(command,"|")
344 if command=="load" then
346 lists=mpd_send(m,"listplaylists",1)
347 res=process_playlists(lists)
349 res=mpd_send(m,"load "..cmd[3],1)
353 if command=="save" and cmd[3] then
354 res=mpd_send(m,"save "..cmd[3],1)
357 if command=="delete" and cmd[3] then
358 res=mpd_send(m,"rm "..cmd[3],1)
361 if command=="edit" then
363 dir=mpd_send(m,"lsinfo \""..cmd[3].."\"",1)
365 dir=mpd_send(m,"lsinfo",1)
367 res=process_directory(dir)
370 if command=="add" then
373 res=mpd_send(m,"add \""..cmd[3].."\"")
380 print("Content-Type: text/plain\r\n")
381 print("MPD server - unknown command "..command)
382 elseif (res['error_msg']) then
383 print("Content-Type: text/plain\r\n")
384 print("MPD server connection error: "..res['error_msg'])
386 print "Content-Type: text/plain\r\n"
387 print(json.encode(res))