X-Git-Url: https://git.rvb.name/mpd-lua.git/blobdiff_plain/bf3bf92221cb7c06bcd93683db53b54b28ec31b5..759671cb4bae0c5a209dc4406f5e5c3b46316375:/mpd.lua~ diff --git a/mpd.lua~ b/mpd.lua~ new file mode 100755 index 0000000..97ac4f4 --- /dev/null +++ b/mpd.lua~ @@ -0,0 +1,417 @@ +#!/usr/bin/lua + +local hasuci,uci = pcall(require,"uci") + +require("socket") +json=require("json") + +function url_decode(str) + if not str then return nil end + str = string.gsub (str, "+", " ") + str = string.gsub (str, "%%(%x%x)", function(h) return + string.char(tonumber(h,16)) end) + str = string.gsub (str, "\r\n", "\n") + return str +end + +function split(s, delimiter) + local result = {}; + for match in (s..delimiter):gmatch("(.-)"..delimiter) do + result[#result+1] = match; + end + return result; +end + +function string.starts(String,Start) + return string.sub(String,1,string.len(Start))==Start +end + +function process_playlist(playlist) + local res={} + for _,record in pairs(playlist) do + local splitted = split(record,": ") + local name = splitted[2] + local splitted = split(splitted[1],":") + local id = splitted[1] + local rectype = splitted[2] + local rec = {} + if not (id == "OK") then + rec["id"] = id + rec["type"] = rectype + rec["name"] = name + res[id] = rec + end + end + return res +end + +function process_playlists(playlists) + local res={} + for _,record in pairs(playlists) do + local splitted = split(record,": ") + if splitted[1]=="playlist" then + res[#res+1] = splitted[2] + end + end + return res +end + +function process_directory(directory) + local res={} + for _,record in pairs(directory) do + local splitted = split(record,": ") + if splitted[1]=="directory" or splitted[1]=="file" then + local rec={} + rec["type"] = splitted[1] + rec["name"] = splitted[2] + res[#res+1] = rec + end + end + return res +end + +function mpd_new(settings) + local client = {} + if settings == nil then settings = {} end + + client.hostname = settings.hostname or "localhost" + client.port = settings.port or 6600 + client.desc = settings.desc or client.hostname + client.password = settings.password + client.timeout = settings.timeout or 1 + client.retry = settings.retry or 60 + + return client +end + +function mpd_send(mpd,action,raw) + + local command = string.format("%s\n", action) + local values = {} + + -- connect to MPD server if not already done. + if not mpd.connected then + local now = os.time(); + if not mpd.last_try or (now - mpd.last_try) > mpd.retry then + mpd.socket = socket.tcp() + mpd.socket:settimeout(mpd.timeout, 't') + mpd.last_try = os.time() + mpd.connected = mpd.socket:connect(mpd.hostname, mpd.port) + if not mpd.connected then + return { errormsg = "could not connect" } + end + mpd.last_error = nil + + -- Read the server's hello message + local line = mpd.socket:receive("*l") + if not line:match("^OK MPD") then -- Invalid hello message? + mpd.connected = false + return { errormsg = string.format("invalid hello message: %s", line) } + else + _, _, mpd.version = string.find(line, "^OK MPD ([0-9.]+)") + end + + -- send the password if needed + if mpd.password then + local rsp = mpd_send(mpd,string.format("password %s", mpd.password)) + if rsp.errormsg then + return rsp + end + end + else + local retry_sec = mpd.retry - (now - mpd.last_try) + return { errormsg = string.format("%s (retrying in %d sec)", mpd.last_error, retry_sec) } + end + end + + mpd.socket:send(command) + + local line = ""; err=0 + while not line:match("^OK$") do + line, err = mpd.socket:receive("*l") + if not line then -- closed,timeout (mpd killed?) + mpd.last_error = err + mpd.connected = false + mpd.socket:close() + return mpd_send(mpd,action) + end + + if line:match("^ACK") then + return { errormsg = line } + end + + if not raw then + + local pattern = string.format("(%s)", ": ") + local i = string.find (line, pattern, 0) + + if i ~= nil then + local key=string.sub(line,1,i-1) + local value=string.sub(line,i+2,-1) + values[string.lower(key)] = value + end + + else + + values[#values+1]=line + + end + end + + return values +end + +if hasuci then + + x = uci.cursor() + + settings = {} + settings['host'] = x.get("mpd","server","host") or "localhost" + settings['port'] = x.get("mpd","server","port") or 6600 + settings['timeout'] = x.get("mpd","server","timeout") or 1 + + volstep = x.get("mpd","control","volume_step") or 3 + + password = x.get("mpd","server","password") + +else + + config = arg[1] + if not config then + config="/etc/mpd-lua.json" + end + + settings={} + local open = io.open + local file = open(config, "r") + if file then + local content = file:read "*a" + file:close() + settings=json.decode(content) + end + + settings['host'] = settings['host'] or "localhost" + settings['port'] = settings["port"] or 6600 + settings['timeout'] = settings["timeout"] or 1 + + volstep = settings["volstep"] or 3 + +end + + +if password then + settings["password"] = password +end + +m = mpd_new(settings) + +command = url_decode(os.getenv('QUERY_STRING')) + +if not command or command=="" then + command="idle" +end + +if command=="play" or command=="pause" or command=="stop" then + + res=mpd_send(m,command) + +elseif command=="previous" or command=="next" then + + res=mpd_send(m,"play") + res=mpd_send(m,command) + +elseif command=="idle" then + + m.timeout=30 + res=mpd_send(m,command) + +elseif command=="vold" then + + status=mpd_send(m,"status") + volume=tonumber(status["volume"]) + res=mpd_send(m,"setvol "..(volume-volstep)) + +elseif command=="volu" then + + status=mpd_send(m,"status") + volume=tonumber(status["volume"]) + res=mpd_send(m,"setvol "..(volume+volstep)) + +elseif string.starts(command,"fastfwd") then + + cmd=split(command,"|") + skip=tonumber(cmd[2]) + if not skip then + skip=15 + end + + status=mpd_send(m,"status") + rec_time=status["time"] + song=status["song"] + + if song then + + if rec_time then + rec_time=split(rec_time,":") + cur_time=tonumber(rec_time[1]) + + track_time=tonumber(rec_time[2]) + cur_time=cur_time+skip + if cur_time>track_time then + cur_time=track_time + end + + mpd_send(m,"seek "..song.." "..cur_time) + + else + + mpd_send(m,"play") + + end + + end + + res={} + +elseif string.starts(command,"rewind") then + + cmd=split(command,"|") + skip=tonumber(cmd[2]) + if not skip then + skip=15 + end + + status=mpd_send(m,"status") + rec_time=status["time"] + song=status["song"] + + if song then + + if rec_time then + rec_time=split(rec_time,":") + cur_time=tonumber(rec_time[1]) + + track_time=tonumber(rec_time[2]) + cur_time=cur_time-skip + if cur_time<0 then + cur_time=0 + end + + mpd_send(m,"seek "..song.." "..cur_time) + + else + + mpd_send(m,"play") + mpd_send(m,"previous") + + end + + end + + res={} + +elseif command=="status" then + + res=mpd_send(m,"status") + song=res["song"] + playlist=mpd_send(m,"playlist",1) + pl=process_playlist(playlist) + + if song then + res['current_playing']=pl[song]['name'] + else + res['song']="--" + res['current_playing']="---" + end + +elseif command=="playlist" then + + playlist=mpd_send(m,"playlist",1) + res=process_playlist(playlist) + +elseif command=="repeat" then + + status=mpd_send(m,"status") + rep=1-status["repeat"] + res=mpd_send(m,"repeat "..rep) + +elseif string.starts(command,"cpl") then + + cmd=split(command,"|") + id=cmd[3] + command=cmd[2] + + if command=="playitem" then + command="play "..id + res=mpd_send(m,command) + end + + if command=="clear" then + res=mpd_send(m,"clear") + end + + if command=="remove" then + command="delete "..id + res=mpd_send(m,command) + end + + if command=="moveup" then + command="swap "..id.." "..(id-1) + res=mpd_send(m,command) + end + + if command=="movedown" then + command="swap "..id.." "..(id+1) + res=mpd_send(m,command) + end + +elseif string.starts(command,"lists") then + + cmd=split(command,"|") + command=cmd[2] + + if command=="load" then + if not cmd[3] then + lists=mpd_send(m,"listplaylists",1) + res=process_playlists(lists) + else + res=mpd_send(m,"load "..cmd[3],1) + end + end + + if command=="save" and cmd[3] then + res=mpd_send(m,"save "..cmd[3],1) + end + + if command=="delete" and cmd[3] then + res=mpd_send(m,"rm "..cmd[3],1) + end + + if command=="edit" then + if cmd[3] then + dir=mpd_send(m,"lsinfo \""..cmd[3].."\"",1) + else + dir=mpd_send(m,"lsinfo",1) + end + res=process_directory(dir) + end + + if command=="add" then + res={} + if cmd[3] then + res=mpd_send(m,"add \""..cmd[3].."\"") + end + end + +end + +if not res then + print("Content-Type: text/plain\r\n") + print("MPD server - unknown command "..command) +elseif (res['error_msg']) then + print("Content-Type: text/plain\r\n") + print("MPD server connection error: "..res['error_msg']) +else + print "Content-Type: text/plain\r\n" + print(json.encode(res)) +end