Include notionflux in the main repo instead of its own submodule
[notion/jeffpc.git] / contrib / statusd / statusd_mocp.lua
blob7e83b95adc0276e4f2738aeef8ff2c672c9bc50e
1 -- Authors: tyranix <tyranix@gmail.com>
2 -- License: Public domain
3 -- Last Changed: Unknown
4 --
5 -- statusd_mocp.lua
6 --[[
7 statusd for moc (Music On Console). This is called mocp for two reasons.
8 First, there is already a statusd_moc.lua. Secondly, the actual executable
9 is called mocp on Debian because moc is taken by Qt.
11 Moc is a great replacement for xmms. It's even better in an ion3 environment
12 because it's all console based.
14 Keys are now dynamically generated by the output from "mocp -i". If mocp adds a
15 new field, it will be statusd.inform()ed. No more messy tables or redundant
16 variables. The keys follow the ion convention and are lowercased.
18 You can also use the new user_defined_* variables. This lets you
19 have a template for when mocp is playing or paused (with lots of information)
20 and a very simple one for when it is off. If you use the keys directly, then
21 you will get a lot of "?" when mocp is off. You can either edit this file
22 directly or edit your cfg_statusbar.lua (see below). I recommend editing your
23 cfg_statusbar.lua.
25 The format of the user_defined_* is to use the normal keys listed below
26 but without the mocp_ prefix. For instance, in your cfg_statusbar.lua file
27 you could have:
29 if not rotate_statusbar.configurations then
30 rotate_statusbar.configurations = {
31 -- Moc meter
32 mocp = {
33 -- Update the statusbar every 2 seconds.
34 update_interval = 2 * 1000,
36 -- Template when moc is playing music
37 user_defined_play = 'moc: %state "%title" '
38 .. "(%currenttime / %totaltime)",
40 -- Template when moc is paused
41 user_defined_pause = 'moc: %state "%songtitle" %bitrate '
42 .. "[%currenttime | %totaltime]",
44 -- State is the only value reported when mocp is stopped.
45 user_defined_stop = "moc: %state",
47 -- State is the only value reported when mocp is off.
48 user_defined_off = "",
51 end
53 And then in your statusbar template, use %mocp_user_defined. It will use
54 one of the user_defined_* templates depending on mocp's state.
56 Keys currently reported by "mocp -i":
57 %mocp_state (can be "PLAY", "PAUSE", "STOP", or "OFF")
58 %mocp_file (e.g. "/music/cdbaby/Celldweller_-_Switchback_-_2001.ogg")
59 %mocp_title (e.g. "Celldweller - Switchback - 2001 (The Beta Cessions)")
60 %mocp_artist (e.g. "Celldweller")
61 %mocp_songtitle (e.g. "Switchback - 2001")
62 %mocp_album (e.g. "The Beta Cessions")
63 %mocp_totaltime (e.g. "04:35")
64 %mocp_timeleft (e.g. "04:22")
65 %mocp_totalsec (e.g. "275")
66 %mocp_currenttime (e.g. "00:13")
67 %mocp_currentsec (e.g. "13")
68 %mocp_bitrate (e.g. "87Kbps")
69 %mocp_rate (e.g. "44KHz")
71 This is not generated by mocp's output. The format is determined by the
72 template in cfg_statusbar.lua.
73 %mocp_user_defined (String based on user_defined_* and moc's state)
75 Default settings you can change from your statusbar configuration:
76 update_interval How frequently to change the status
77 socket_dir Where mocp's socket is relative to ion's working directory
78 command Command to execute to get information from mocp
79 user_defined_play Statusbar template when mocp is playing
80 user_defined_pause Statusbar template when mocp is paused
81 user_defined_stop Statusbar template when mocp is stopped
82 user_defined_off Statusbar template when mocp is not running
85 Usage:
87 1) If you do not have ~/.ion3/cfg_statusbar.lua, copy that file from Ion3 to
88 your ~/.ion3 directory. On Debian, it is in /etc/X11/ion3/cfg_statusbar.lua.
90 2) Ion3 will load the appropriate modules if they are in the template at
91 startup.
93 So place one or more of the above %mocp_* fields into the template in
94 ~/.ion3/cfg_statusbar.lua.
96 3) You can use the user_defined_* keys instead of using the keys
97 in step #2. To do this, open up ~/.ion3/cfg_statusbar.lua and go to
98 the section with the defaults. Add on_template and off_template in the
99 table for mocp (you may have to add one similar to the load or mail).
101 Then in your ~/.ion3/cfg_statusbar.lua, simply add %mocp_user_defined
102 and it will expand either user_defined_* at runtime.
104 4) Restart ion3 (Hit F12 and type 'session/restart')
106 Alternative Usage: you could use rotate_statusbar.lua so you can rotate
107 between templates. This is useful when you have too much information for
108 one statusbar template or you only care to know the info periodically.
110 To do this, copy rotate_statusbar.lua to ~/.ion3/cfg_statusbar.lua and
111 do steps 2-4 above.
114 You can test this out independent of ion. This will print all of
115 the key=value pairs statusd.inform() was sent.
117 1) Copy statusd_mocp.lua to ~/.ion3/
119 2) Run '/usr/lib/ion3/ion-statusd -m mocp'
120 This will dump out all of the updates to the terminal as they happen
122 3) Hit control+c when you are done testing.
125 All public domain based on statusd_moc.lua. It also borrows some
126 ideas from rss_feed.lua which is public domain too. I used the idea of
127 an on/off template from statusd_mocmon.lua.
130 tyranix [tyranix at gmail]
132 --]]
134 local defaults={
135 -- Update every 2 seconds
136 update_interval=2*1000,
138 -- ~/.moc is where moc stores preferences and the socket
139 socket_dir=os.getenv("HOME").."/.moc",
141 -- Command to get information from mocp.
142 command="mocp -i 2>/dev/null",
144 -- User defined template. If mocp is off, we replace it with the other
145 -- template. This template format is similar to the statusbar template
146 -- except that I only replace %mocp_* keys. Leave off the mocp_ part
147 -- because we don't need that here.
148 user_defined_play = 'moc: %state "%title" (%currenttime / %totaltime)',
150 -- User defined template when moc is paused.
151 user_defined_pause = 'moc: %state "%songtitle" %bitrate '
152 .. "[%currenttime | %totaltime]",
154 -- User defined template when moc is stopped. Only the state key can be
155 -- replaced in this string.
156 user_defined_stop = "moc: %state",
158 -- User defined template. This is what is displayed if moc is turned off.
159 -- Only the state key can be replaced.
160 -- When moc isn't running, I prefer to not display anything.
161 user_defined_off = "",
164 local mocp_timer = nil
165 local mocp_values = {}
167 -- Overwrite our defaults with the user's settings
168 local settings = table.join(statusd.get_config("mocp"), defaults)
170 -- This must come after the above settings definition.
171 local mocp_templates = {
172 PLAY = settings.user_defined_play,
173 PAUSE = settings.user_defined_pause,
174 STOP = settings.user_defined_stop,
175 OFF = settings.user_defined_off,
178 -- Used by gsub on each pattern matched. This sets the values.
179 -- 'name' and 'setting' are the two referenced patterns (in parentheses).
180 local function set_mocp_values(name, value)
181 mocp_values[string.lower(name)] = value
184 -- Used by gsub on each pattern matched. This returns the value for a template.
185 local function get_mocp_values(name)
186 if not mocp_values[name] then
187 return "?"
188 else
189 return mocp_values[name]
193 -- Tell statusd to update its keys with our new values.
194 local function inform_statusd(mocp_status)
195 local name, value
196 mocp_values = {}
198 -- If we didn't get any output from mocp, then it is turned off.
199 if not mocp_status or mocp_status == "" then
200 mocp_values["state"] = "OFF"
201 else
202 -- Go through the output from 'mocp -i' and find the values.
203 -- All of mocp's output has this format: Name: setting\n
204 -- Even when there is no setting, there's always a space.
205 -- The 20 at the end prevents it from going into an infinite loop if
206 -- the user specified some strange option. It makes at most 20
207 -- substitutions.
208 string.gsub(mocp_status, "(%w+): ([^\n]*)", set_mocp_values, 20)
211 -- Report all of the known values from mocp -i (not user defined).
212 -- For off/stopped, it is only the state.
213 for name, value in pairs(mocp_values) do
214 statusd.inform("mocp_" .. name, value)
217 -- Point to the right user defined template depending on moc's state
218 local template = mocp_templates[mocp_values["state"]];
219 if not template then
220 template = "statusd_mocp: Error! Invalid state '"
221 .. mocp_values["state"] .. "' found"
224 -- Report the user's template
225 local result = string.gsub(template, "%%([a-z0-9_]+)", get_mocp_values)
226 statusd.inform("mocp_user_defined", result)
229 -- Continually read input until partial_data is nil then parse it.
230 local function read_from_mocp(partial_data)
231 -- statusd.popen_bgread() will return data as it becomes available.
232 -- The easiest way to handle this is to keep concatenating the data
233 -- until it runs out. There's no guarantee that popen_bgread() will
234 -- return full lines (up to a newline).
235 local mocp_status = ""
236 while partial_data do
237 mocp_status = mocp_status .. partial_data
238 -- Yield until we get more input. popen_bgread will call resume on us.
239 partial_data = coroutine.yield()
242 -- After we have read all the data, then inform statusd.
243 inform_statusd(mocp_status)
245 -- Continually call the update function.
246 mocp_timer:set(settings.update_interval, update_mocp)
249 -- Continually read from stderr. It just prints it out to stdout now.
250 -- This should never be called because we're dumping it to /dev/null.
251 local function print_stderr(partial_data)
252 local mocp_error = ""
253 while partial_data do
254 mocp_error = mocp_error .. partial_data
255 -- Yield until we get more input. popen_bgread will call resume on us.
256 partial_data = coroutine.yield()
258 print(mocp_error, "\n")
261 -- Main loop for mocp.
262 function update_mocp()
263 -- If there is no PID file, then moc is turned off.
264 local f=io.open(settings.socket_dir.."/pid")
265 if not f then
266 -- mocp is turned off
267 read_from_mocp()
268 else
269 f:close()
271 -- Tell ion to start up mocp and keep reading partial
272 -- chunks until it is complete. This is better for
273 -- performance because ion doesn't have to block here.
274 statusd.popen_bgread(settings.command,
275 coroutine.wrap(read_from_mocp),
276 coroutine.wrap(print_stderr))
280 -- Timer so we can keep telling statusd what the current value is.
281 mocp_timer = statusd.create_timer()
282 update_mocp()