3 local hasuci,uci = pcall(require,"uci")
8 function url_decode(str)
9 if not str then return nil end
10 str = string.gsub (str, "+", " ")
11 str = string.gsub (str, "%%(%x%x)", function(h) return
12 string.char(tonumber(h,16)) end)
13 str = string.gsub (str, "\r\n", "\n")
17 function split(s, delimiter)
19 for match in (s..delimiter):gmatch("(.-)"..delimiter) do
20 result[#result+1] = match;
25 function string.starts(String,Start)
26 return string.sub(String,1,string.len(Start))==Start
29 function process_playlist(playlist)
31 for _,record in pairs(playlist) do
32 local splitted = split(record,": ")
33 local name = splitted[2]
34 local splitted = split(splitted[1],":")
35 local id = splitted[1]
36 local rectype = splitted[2]
38 if not (id == "OK") then
48 function process_playlists(playlists)
50 for _,record in pairs(playlists) do
51 local splitted = split(record,": ")
52 if splitted[1]=="playlist" then
53 res[#res+1] = splitted[2]
59 function process_directory(directory)
61 for _,record in pairs(directory) do
62 local splitted = split(record,": ")
63 if splitted[1]=="directory" or splitted[1]=="file" then
65 rec["type"] = splitted[1]
66 rec["name"] = splitted[2]
73 function mpd_new(settings)
75 if settings == nil then settings = {} end
77 client.hostname = settings.hostname or "localhost"
78 client.port = settings.port or 6600
79 client.desc = settings.desc or client.hostname
80 client.password = settings.password
81 client.timeout = settings.timeout or 1
82 client.retry = settings.retry or 60
87 function mpd_send(mpd,action,raw)
89 local command = string.format("%s\n", action)
92 -- connect to MPD server if not already done.
93 if not mpd.connected then
94 local now = os.time();
95 if not mpd.last_try or (now - mpd.last_try) > mpd.retry then
96 mpd.socket = socket.tcp()
97 mpd.socket:settimeout(mpd.timeout, 't')
98 mpd.last_try = os.time()
99 mpd.connected = mpd.socket:connect(mpd.hostname, mpd.port)
100 if not mpd.connected then
101 return { errormsg = "could not connect" }
105 -- Read the server's hello message
106 local line = mpd.socket:receive("*l")
107 if not line:match("^OK MPD") then -- Invalid hello message?
108 mpd.connected = false
109 return { errormsg = string.format("invalid hello message: %s", line) }
111 _, _, mpd.version = string.find(line, "^OK MPD ([0-9.]+)")
114 -- send the password if needed
116 local rsp = mpd_send(mpd,string.format("password %s", mpd.password))
122 local retry_sec = mpd.retry - (now - mpd.last_try)
123 return { errormsg = string.format("%s (retrying in %d sec)", mpd.last_error, retry_sec) }
127 mpd.socket:send(command)
129 local line = ""; err=0
130 while not line:match("^OK$") do
131 line, err = mpd.socket:receive("*l")
132 if not line then -- closed,timeout (mpd killed?)
134 mpd.connected = false
136 return mpd_send(mpd,action)
139 if line:match("^ACK") then
140 return { errormsg = line }
145 local pattern = string.format("(%s)", ": ")
146 local i = string.find (line, pattern, 0)
149 local key=string.sub(line,1,i-1)
150 local value=string.sub(line,i+2,-1)
151 values[string.lower(key)] = value
156 values[#values+1]=line
169 settings['host'] = x.get("mpd","server","host") or "localhost"
170 settings['port'] = x.get("mpd","server","port") or 6600
171 settings['timeout'] = x.get("mpd","server","timeout") or 1
173 volstep = x.get("mpd","control","volume_step") or 3
175 password = x.get("mpd","server","password")
181 config="/etc/mpd-lua.json"
186 local file = open(config, "r")
188 local content = file:read "*a"
190 settings=json.decode(content)
193 settings['host'] = settings['host'] or "localhost"
194 settings['port'] = settings["port"] or 6600
195 settings['timeout'] = settings["timeout"] or 1
197 volstep = settings["volstep"] or 3
203 settings["password"] = password
206 m = mpd_new(settings)
208 command = url_decode(os.getenv('QUERY_STRING'))
210 if not command or command=="" then
214 if command=="play" or command=="pause" or command=="stop" then
216 res=mpd_send(m,command)
218 elseif command=="previous" or command=="next" then
220 res=mpd_send(m,"play")
221 res=mpd_send(m,command)
223 elseif command=="idle" then
226 res=mpd_send(m,command)
228 elseif command=="vold" then
230 status=mpd_send(m,"status")
231 volume=tonumber(status["volume"])
232 res=mpd_send(m,"setvol "..(volume-volstep))
234 elseif command=="volu" then
236 status=mpd_send(m,"status")
237 volume=tonumber(status["volume"])
238 res=mpd_send(m,"setvol "..(volume+volstep))
240 elseif string.starts(command,"fastfwd") then
242 cmd=split(command,"|")
243 skip=tonumber(cmd[2])
248 status=mpd_send(m,"status")
249 rec_time=status["time"]
255 rec_time=split(rec_time,":")
256 cur_time=tonumber(rec_time[1])
258 track_time=tonumber(rec_time[2])
259 cur_time=cur_time+skip
260 if cur_time>track_time then
264 mpd_send(m,"seek "..song.." "..cur_time)
276 elseif string.starts(command,"rewind") then
278 cmd=split(command,"|")
279 skip=tonumber(cmd[2])
284 status=mpd_send(m,"status")
285 rec_time=status["time"]
291 rec_time=split(rec_time,":")
292 cur_time=tonumber(rec_time[1])
294 track_time=tonumber(rec_time[2])
295 cur_time=cur_time-skip
300 mpd_send(m,"seek "..song.." "..cur_time)
305 mpd_send(m,"previous")
313 elseif command=="status" then
315 res=mpd_send(m,"status")
317 playlist=mpd_send(m,"playlist",1)
318 pl=process_playlist(playlist)
321 res['current_playing']=pl[song]['name']
323 res['current_playing']="---"
326 elseif command=="playlist" then
328 playlist=mpd_send(m,"playlist",1)
329 res=process_playlist(playlist)
331 elseif command=="repeat" then
333 status=mpd_send(m,"status")
334 rep=1-status["repeat"]
335 res=mpd_send(m,"repeat "..rep)
337 elseif string.starts(command,"cpl") then
339 cmd=split(command,"|")
343 if command=="playitem" then
345 res=mpd_send(m,command)
348 if command=="clear" then
349 res=mpd_send(m,"clear")
352 if command=="remove" then
353 command="delete "..id
354 res=mpd_send(m,command)
357 if command=="moveup" then
358 command="swap "..id.." "..(id-1)
359 res=mpd_send(m,command)
362 if command=="movedown" then
363 command="swap "..id.." "..(id+1)
364 res=mpd_send(m,command)
367 elseif string.starts(command,"lists") then
369 cmd=split(command,"|")
372 if command=="load" then
374 lists=mpd_send(m,"listplaylists",1)
375 res=process_playlists(lists)
377 res=mpd_send(m,"load "..cmd[3],1)
381 if command=="save" and cmd[3] then
382 res=mpd_send(m,"save "..cmd[3],1)
385 if command=="delete" and cmd[3] then
386 res=mpd_send(m,"rm "..cmd[3],1)
389 if command=="edit" then
391 dir=mpd_send(m,"lsinfo \""..cmd[3].."\"",1)
393 dir=mpd_send(m,"lsinfo",1)
395 res=process_directory(dir)
398 if command=="add" then
401 res=mpd_send(m,"add \""..cmd[3].."\"")
408 print("Content-Type: text/plain\r\n")
409 print("MPD server - unknown command "..command)
410 elseif (res['error_msg']) then
411 print("Content-Type: text/plain\r\n")
412 print("MPD server connection error: "..res['error_msg'])
414 print "Content-Type: text/plain\r\n"
415 print(json.encode(res))