examples/mpd.lua
author Myhailo Danylenko <isbear@ukrpost.net>
Wed, 28 Nov 2012 20:17:53 +0200
changeset 146 04d19c9c1196
parent 69 ab6d4ee8974c
permissions -rw-r--r--
Fix module loading problem


-- MPD INTERATION

-- library

local socket = require 'socket'

--

local O = {
	hostname     = "localhost",
	password     = nil,
	port         = 6600,
}

local F = { }

-- separator allows split output into records, that are started by any of present in separator keys
-- returns table of field (lowercase) - value records
-- command status is returned in STATUS field
-- on unexpected errors returns just false, dropping any available data
function F.receive_message ( tcp, separator )
	local ret  = {}
	local line = tcp:receive ( '*l' )
	while line and not ( line:match ( '^OK' ) or line:match ( '^ACK' ) ) do
		local key, val = line:match ( '^(.-):%s*(.*)$' )
		if key then
			if separator then
				key = key:lower ()
				if separator[key] then
					table.insert ( ret, {} )
				end
				ret[#ret][key]   = val
			else
				ret[key:lower()] = val
			end
		end
		line = tcp:receive ( '*l' )
	end
	if not line then
		return false -- an error occured, ret, most likely, does not contains exact and complete info...
	else
		ret.STATUS = line:match ( '^%S+' )
		return ret
	end
end

-- use: mpd.call_command { 'status' }
--      mpd.call_command { 'lsinfo misc', list = { file = true, directory = true } }
--      mpd.call_command { 'next', noret = true } -- removed, early termination with password :/
--      mpd.call_command { 'status', 'currentsong' }
-- on one command returns just results of that command, on multi - array of results
-- on errors returns nil
-- noret can terminate socket too early, thus, do not use it with lists of commands
function F.call_command ( opts )
	local tcp = socket.tcp ()
	if not tcp then
		print ( 'mpd: cannot get master tcp object' )
		return nil
	end

	if not O.hostname then
		local server = os.getenv ( 'MPD_HOST' )
		if server then
			local password, host = server:match ( '(.+)@(.-)' )
			if password then
				F.server ( host, os.getenv ( 'MPD_PORT' ) or 6600, password )
			else
				F.server ( server, os.getenv ( 'MPD_PORT' ) or 6600, nil )
			end
		else
			F.server ( "localhost", 6600, nil )
		end
	end

	if not tcp:connect ( O.hostname, O.port ) then
		tcp:close ()
		print ( 'mpd: cannot connect to server' )
		return nil
	end

	local ret = {}
	ret = F.receive_message ( tcp )
	if not ret then
		tcp:close ()
		print ( 'mpd: error getting greeting from server' )
		return nil
	elseif ret.STATUS ~= 'OK' then
		print ( 'mpd: server ack\'s in greeting' )
	end

	if O.password then
		if not tcp:send ( "password " .. O.password .. "\n" ) then
			tcp:close ()
			print ( 'mpd: error sending password' )
			return nil
		end
		ret = mpd.receive_message ( tcp )
		if not ret then
			tcp:close ()
			print ( 'mpd: error getting response for password' )
			return nil
		elseif ret.STATUS ~= 'OK' then
			print ( 'mpd: server refuses password' )
		end
	end

	for num, command in ipairs ( opts ) do
		if not tcp:send ( command .. "\n" ) then
			tcp:close ()
			print ( 'mpd: error sending command ' .. command )
			return nil
		end
		ret[num] = F.receive_message ( tcp, opts.list )
		if not ret[num] then
			print ( 'mpd: error getting result' )
		end
		if ret[num]['STATUS'] ~= 'OK' then
			print ( 'mpd: server acks our command ' .. command )
		end
	end

	tcp:close ()
	if #ret > 1 then
		return ret
	else
		return ret[1]
	end
end

function F.server ( host, port, password )
	O.hostname = host
	O.port     = port
	O.password = password
end

return F

-- vim: se ts=4: --