examples/mpd.lua
author Myhailo Danylenko <isbear@ukrpost.net>
Mon, 23 Feb 2009 23:23:42 +0200
changeset 5 cba039bd6f13
child 7 eb6d89bf1fbf
permissions -rw-r--r--
Included sample configuration into package


-- Requires libsocket

-- TODO:
-- do pubsub tunes+notify instead of status hacking

require 'socket'

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

mpd = {}

-- 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 mpd.receive_message ( tcp, separator )
	local ret  = {}
	local line = tcp:receive ( '*l' )
	while line and not ( line:find ( '^OK' ) or line:find ( '^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 }
--      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 mpd.call_command ( opts )
	local tcp = socket.tcp ()
	if not tcp then
		print ( 'mpd: cannot get master tcp object' )
		return nil
	elseif not tcp:connect ( settings.hostname, settings.port ) then
		tcp:close ()
		print ( 'mpd: cannot connect to server' )
		return nil
	end

	local ret = {}
	if not opts.noret then
		ret = mpd.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
	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
		if not opts.noret then
			ret[num] = mpd.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
	end

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

-- MCABBER PART --

mpd_enabled = false

function mpd_getstatus ()
	if not mpd_enabled then
		return ''
	end
	
	local stats = mpd.call_command { 'status', 'currentsong' }
	if stats[1].state ~= 'play' and stats[1].state ~= 'pause' then
		return ''
	end

	local title = stats[2].title
	if not title then
		if stats[2].file then
			title = stats[2].file
		else
			title = ''
		end
	elseif not stats[2].artist then
		title = string.format ( "%s (%s)", title, stats[2].file )
	else
		title = string.format ( "%s - %s", stats[2].artist, title )
	end

	if stats[1].state == 'pause' then
		return string.format ( "[mpd: <зупинено> %s]", title )
	else
		return string.format ( "[mpd: %s]", title )
	end
end

function parse_status ()
	local stletter, stmessage = main.status ()
	local cmd = char2status[stletter]
	local message, mpd_string = stmessage:match ( "^(.-)%s+(%[mpd:%s+.+%s*%])" )
	if message then
		return cmd, message, mpd_string
	else
		return cmd, stmessage, ''
	end
end

function mpd_callback ()
	local new_mpd_string = mpd_getstatus ()
	local status, message, mpd_string = parse_status ()
	if new_mpd_string ~= mpd_string then
		main.run ( string.format ( 'status %s %s %s', status, message, new_mpd_string ) )
	end
	if mpd_enabled then
		return true
	else
		return false
	end
end

-- do not call it too fast, or you end up with many daemons at once
function enable_mpd ( yn )
	if yn == nil then
		yn = true
	end
	if yn then
		if not mpd_enabled then
			main.timer ( 15, mpd_callback )
			mpd_enabled = true
			-- update status
		end
	else
		if mpd_enabled then
			mpd_enabled = false
			-- update status
		end
	end
end

main.add_command ( 'mpd',
	function ( args )
		local enable = yesno ( args )
		if enable == nil then
			if mpd_enabled then
				print ( "MPD status string is enabled" )
			else
				print ( "MPD status string is disabled" )
			end
		else
			enable_mpd ( enable )
		end
	end )

commands_help['mpd'] = "[enable|disable|on|off|yes|no|true|false]\n\nSets state of mpd string in your status.\nIf state is omitted, prints current state."

-- vim: se ts=4: --