1 -- statusd for MPD (Music Player Daemon)
2 -- bugs/requests/comments: delirium@hackish.org
4 -- requires that netcat is available in the path
10 -- 500 or less makes seconds increment relatively smoothly while playing
11 update_interval = 1000,
13 -- mpd server info (localhost:6600 are mpd defaults)
14 address = "localhost",
17 -- mpd password (if any)
20 -- seconds to consider the mpd in 'important' hint (0 never, -1 always)
21 important_time = 10, -- seconds
25 -- can use the following:
26 -- track metadata: %artist, %title, %num, %album, %year, %len
27 -- current track position: %pos
29 -- escape for the percent character: %%
32 template = "%artist - %num - %title (%pos / %len)"
35 local settings = table.join(statusd.get_config("mpd"), defaults)
41 -- Function for starting and restarting execution of a process
43 local function start_execution()
45 -- Set up a function for receiving the data
46 -- and informing the statusbar
48 local function process_input(data)
50 -- At first, we are not initialized
53 -- When we get new data, output the last complete line
59 if string.sub(data, 1, 6) ~= "OK MPD" then
60 statusd.inform("mpd_hint", "critical")
61 statusd.inform("mpd", "bad mpd response on init")
64 -- 'password' response (if necessary)
65 if settings.password ~= nil then
66 if string.sub(data, 1, 2) ~= "OK" then
67 statusd.inform("mpd_hint", "critical")
68 statusd.inform("mpd_hint", "bad mpd password")
72 -- everything ok, receive more data
74 data = coroutine.yield()
80 for attrib, val in string.gfind(data, "([^\n]-): ([^\n]*)") do
82 if attrib == "time" then
83 _, _, info.pos, info.len = string.find(val, "(%d+):(%d+)")
84 if settings.important_time == -1
85 or info.pos+0 <= settings.important_time
86 and settings.important_time ~= 0 then
89 info.pos = string.format("%d:%02d", math.floor(info.pos / 60),
90 math.mod(info.pos, 60))
91 info.len = string.format("%d:%02d", math.floor(info.len / 60),
92 math.mod(info.len, 60))
93 elseif attrib == "state" then
95 if info.state ~= last_state then
98 last_state = info.state
99 elseif attrib == "volume" then
100 info.volume = val .. "%"
101 if info.volume ~= last_volume then
104 last_volume = info.volume
105 elseif attrib == "Artist" then
107 elseif attrib == "Title" then
109 elseif attrib == "Album" then
111 elseif attrib == "Track" then
113 elseif attrib == "Date" then
118 -- done querying; now build the string
119 statusd.inform("mpd_hint", hint)
120 if info.state == "play" then
121 local mpd_st = settings.template
123 mpd_st = string.gsub(mpd_st, "%%([%w%_]+)",
124 function (x) return(info[x] or "") end)
125 mpd_st = string.gsub(mpd_st, "%%%%", "%%")
126 statusd.inform("mpd", mpd_st)
127 elseif info.state == "pause" then
128 statusd.inform("mpd", "Paused")
130 statusd.inform("mpd", "No song playing")
133 -- Wait for bgread to signal that more data
134 -- is available or the program has exited
136 data = coroutine.yield()
139 -- Program has exited.
140 -- Start a timer for a new execution if set.
142 timer:set(settings.update_interval,
143 function() start_execution() end)
146 -- Set up a simple function for printing errors
148 local function process_error(data)
150 io.stderr:write("ion-statusd [statusd_mpd]: " .. data)
152 data = coroutine.yield()
156 -- Execute the program in the background and create
157 -- coroutine functions to handle input and error data
158 local sleep = settings.update_interval / 1000
159 sleep = string.gsub(sleep, '(%d+),(%d+)', '%1.%2')
160 local cmd = 'while sleep ' .. sleep .. '; do ' ..
161 'echo "status\ncurrentsong"; done | ' ..
162 settings.netcat .. ' ' .. settings.address .. ' ' ..
164 if settings.password ~= nil then
165 cmd = "echo 'password " .. settings.password .. "'; " .. cmd
167 statusd.popen_bgread(cmd, coroutine.wrap(process_input),
168 coroutine.wrap(process_error))
172 -- Now start execution of all defined processes
174 timer = statusd.create_timer()
177 -- vim: set et sts=4 sw=4 :