scripts/mpd.lua
changeset 4 bca17e4a9851
equal deleted inserted replaced
3:a5f864d4207f 4:bca17e4a9851
       
     1 
       
     2 -- Requires libsocket
       
     3 
       
     4 -- TODO:
       
     5 -- do pubsub tunes+notify instead of status hacking
       
     6 
       
     7 require 'socket'
       
     8 
       
     9 local settings = {
       
    10 	hostname     = "localhost",
       
    11 	password     = nil,
       
    12 	port         = 6600,
       
    13 }
       
    14 
       
    15 mpd = {}
       
    16 
       
    17 -- separator allows split output into records, that are started by any of present in separator keys
       
    18 -- returns table of field (lowercase) - value records
       
    19 -- command status is returned in STATUS field
       
    20 -- on unexpected errors returns just false, dropping any available data
       
    21 function mpd.receive_message ( tcp, separator )
       
    22 	local ret  = {}
       
    23 	local line = tcp:receive ( '*l' )
       
    24 	while line and not ( line:find ( '^OK' ) or line:find ( '^ACK' ) ) do
       
    25 		local key, val = line:match ( '^(.-):%s*(.*)$' )
       
    26 		if key then
       
    27 			if separator then
       
    28 				key = key:lower ()
       
    29 				if separator[key] then
       
    30 					table.insert ( ret, {} )
       
    31 				end
       
    32 				ret[#ret][key]   = val
       
    33 			else
       
    34 				ret[key:lower()] = val
       
    35 			end
       
    36 		end
       
    37 		line = tcp:receive ( '*l' )
       
    38 	end
       
    39 	if not line then
       
    40 		return false -- an error occured, ret, most likely, does not contains exact and complete info...
       
    41 	else
       
    42 		ret.STATUS = line:match ( '^%S+' )
       
    43 		return ret
       
    44 	end
       
    45 end
       
    46 
       
    47 -- use: mpd.call_command { 'status' }
       
    48 --      mpd.call_command { 'lsinfo misc', list = { file = true, directory = true } }
       
    49 --      mpd.call_command { 'next', noret = true }
       
    50 --      mpd.call_command { 'status', 'currentsong' }
       
    51 -- on one command returns just results of that command, on multi - array of results
       
    52 -- on errors returns nil
       
    53 -- noret can terminate socket too early, thus, do not use it with lists of commands
       
    54 function mpd.call_command ( opts )
       
    55 	local tcp = socket.tcp ()
       
    56 	if not tcp then
       
    57 		print ( 'mpd: cannot get master tcp object' )
       
    58 		return nil
       
    59 	elseif not tcp:connect ( settings.hostname, settings.port ) then
       
    60 		tcp:close ()
       
    61 		print ( 'mpd: cannot connect to server' )
       
    62 		return nil
       
    63 	end
       
    64 
       
    65 	local ret = {}
       
    66 	if not opts.noret then
       
    67 		ret = mpd.receive_message ( tcp )
       
    68 		if not ret then
       
    69 			tcp:close ()
       
    70 			print ( 'mpd: error getting greeting from server' )
       
    71 			return nil
       
    72 		elseif ret.STATUS ~= 'OK' then
       
    73 			print ( 'mpd: server ack\'s in greeting' )
       
    74 		end
       
    75 	end
       
    76 
       
    77 	for num, command in ipairs ( opts ) do
       
    78 		if not tcp:send ( command .. "\n" ) then
       
    79 			tcp:close ()
       
    80 			print ( 'mpd: error sending command ' .. command )
       
    81 			return nil
       
    82 		end
       
    83 		if not opts.noret then
       
    84 			ret[num] = mpd.receive_message ( tcp, opts.list )
       
    85 			if not ret[num] then
       
    86 				print ( 'mpd: error getting result' )
       
    87 			end
       
    88 			if ret[num]['STATUS'] ~= 'OK' then
       
    89 				print ( 'mpd: server acks our command ' .. command )
       
    90 			end
       
    91 		end
       
    92 	end
       
    93 
       
    94 	tcp:close ()
       
    95 	if #ret > 1 then
       
    96 		return ret
       
    97 	else
       
    98 		return ret[1]
       
    99 	end
       
   100 end
       
   101 
       
   102 -- MCABBER PART --
       
   103 
       
   104 mpd_enabled = false
       
   105 
       
   106 function mpd_getstatus ()
       
   107 	if not mpd_enabled then
       
   108 		return ''
       
   109 	end
       
   110 	
       
   111 	local stats = mpd.call_command { 'status', 'currentsong' }
       
   112 	if stats[1].state ~= 'play' and stats[1].state ~= 'pause' then
       
   113 		return ''
       
   114 	end
       
   115 
       
   116 	local title = stats[2].title
       
   117 	if not title then
       
   118 		if stats[2].file then
       
   119 			title = stats[2].file
       
   120 		else
       
   121 			title = ''
       
   122 		end
       
   123 	elseif not stats[2].artist then
       
   124 		title = string.format ( "%s (%s)", title, stats[2].file )
       
   125 	else
       
   126 		title = string.format ( "%s - %s", stats[2].artist, title )
       
   127 	end
       
   128 
       
   129 	if stats[1].state == 'pause' then
       
   130 		return string.format ( "[mpd: <зупинено> %s]", title )
       
   131 	else
       
   132 		return string.format ( "[mpd: %s]", title )
       
   133 	end
       
   134 end
       
   135 
       
   136 function parse_status ()
       
   137 	local stletter, stmessage = main.status ()
       
   138 	local cmd = char2status[stletter]
       
   139 	local message, mpd_string = stmessage:match ( "^(.-)%s+(%[mpd:%s+.+%s*%])" )
       
   140 	if message then
       
   141 		return cmd, message, mpd_string
       
   142 	else
       
   143 		return cmd, stmessage, ''
       
   144 	end
       
   145 end
       
   146 
       
   147 function mpd_callback ()
       
   148 	local new_mpd_string = mpd_getstatus ()
       
   149 	local status, message, mpd_string = parse_status ()
       
   150 	if new_mpd_string ~= mpd_string then
       
   151 		main.run ( string.format ( 'status %s %s %s', status, message, new_mpd_string ) )
       
   152 	end
       
   153 	if mpd_enabled then
       
   154 		return true
       
   155 	else
       
   156 		return false
       
   157 	end
       
   158 end
       
   159 
       
   160 -- do not call it too fast, or you end up with many daemons at once
       
   161 function enable_mpd ( yn )
       
   162 	if yn == nil then
       
   163 		yn = true
       
   164 	end
       
   165 	if yn then
       
   166 		if not mpd_enabled then
       
   167 			main.timer ( 15, mpd_callback )
       
   168 			mpd_enabled = true
       
   169 			-- update status
       
   170 		end
       
   171 	else
       
   172 		if mpd_enabled then
       
   173 			mpd_enabled = false
       
   174 			-- update status
       
   175 		end
       
   176 	end
       
   177 end
       
   178 
       
   179 main.add_command ( 'mpd',
       
   180 	function ( args )
       
   181 		local enable = yesno ( args )
       
   182 		if enable == nil then
       
   183 			if mpd_enabled then
       
   184 				print ( "MPD status string is enabled" )
       
   185 			else
       
   186 				print ( "MPD status string is disabled" )
       
   187 			end
       
   188 		else
       
   189 			enable_mpd ( enable )
       
   190 		end
       
   191 	end )
       
   192 
       
   193 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."
       
   194 
       
   195 -- vim: se ts=4: --