Include notionflux in the main repo instead of its own submodule
[notion/jeffpc.git] / contrib / statusd / statusd_mpd-socket.lua
blobc4b6c3951ffa2b72dc7fee464d17ad4c7b424319
1 -- Authors: Marc Hartstein <marc.hartstein@alum.vassar.edu>
2 -- License: Unknown
3 -- Last Changed: Unknown
4 --
5 -- statusd for MPD (Music Player Daemon)
6 -- by Marc Hartstein <marc.hartstein@alum.vassar.edu>
7 --
8 -- version 1.0
9 --
10 -- Based on a script by delirium@hackish.org
12 -- Feel free to contact me with any bugs or suggestions for improvement.
14 -- requires LuaSocket
15 -- http://www.tecgraf.puc-rio.br/~diego/professional/luasocket/home.html
17 -- INSTALLATION
19 -- statusd_mpd-socket.lua is intended as a drop-in replacement for
20 -- statusd_mpd.lua. It should work with your existing statusd_mpd.lua
21 -- configuration, if any.
23 -- To achieve this, statusd_mpd-socket.lua needs to be located by statusd as
24 -- statusd_mpd.lua
26 -- The easiest way to accomplish this is to create a symbolic link in your
27 -- ~/.ion3 directory from statusd_mpd.lua -> wherever you have placed
28 -- statusd_mpd-socket.lua
30 -- Don't forget to include %mpd in a statusbar template in cfg_statusbar.lua
32 -- CONFIGURATION
34 -- See the defaults table below for the configurable settings and their default
35 -- values. You should create a mpd table in cfg_statusbar.lua to customize
36 -- these settings as for any other statusd plugin.
38 local defaults={
39 -- 500 or less makes seconds increment relatively smoothly while playing
40 update_interval = 500,
42 -- how long to go to sleep when we can't talk to mpd so we don't spam the
43 -- system with connection attempts every half-second
44 retry_interval = 60*1000, -- 1m
46 -- mpd server info (localhost:6600 are mpd defaults)
47 address = "localhost",
48 port = 6600,
50 -- mpd password (if any)
51 password = nil,
53 -- display template
54 -- ---
55 -- can use the following:
56 -- track metadata: %artist, %title, %num, %album, %year, %len
57 -- conditional metadata: %artist_or_album
58 -- current track position: %pos
59 -- escape for the percent character: %%
61 -- %artist_or_album will display the artist if any, otherwise it will
62 -- display the album name. I find this useful for Broadway recordings.
64 -- a default template
65 template = "%artist - %num - %title (%pos / %len)"
68 local settings = table.join(statusd.get_config("mpd"), defaults)
70 local mpd_timer
71 local last_success
73 -- load namespace
74 local socket = require("socket")
75 local mpd_socket
77 -- set up a try function which closes the socket if there's an error
78 local try = socket.newtry(function() mpd_socket:close() end)
80 local open_socket = socket.protect(function()
81 -- connect to the server
82 mpd_socket = socket.try(socket.connect(settings.address, settings.port))
84 mpd_socket:settimeout(100)
86 local data -- buffer for reads
88 data = try(mpd_socket:receive())
89 if data == nil or string.sub(data,1,6) ~= "OK MPD" then
90 mpd_socket:close()
91 return nil, "mpd not running"
92 end
94 -- send password (if necessary)
95 if settings.password ~= nil then
96 try(mpd_socket:send("password " .. settings.password .. "\n"))
98 repeat
99 data = try(mpd_socket:receive())
100 until data == nil or string.sub(data,1,2) == "OK" or string.sub(data,1,3) == "ACK"
101 if data == nil or string.sub(data,1,2) ~= "OK" then
102 mpd_socket:close()
103 return nil, "bad mpd password"
107 return true
108 end)
111 local get_mpd_status = socket.protect(function()
113 local data -- buffer for reads
114 local success = false
115 local info = {}
117 -- 'status'
118 -- %pos, %len, and current state (paused/stopped/playing)
119 try(mpd_socket:send("status\n"))
120 repeat
121 data = try(mpd_socket:receive())
122 if data == nil then break end
124 local _,_,attrib,val = string.find(data, "(.-): (.*)")
125 if attrib == "time" then
126 _,_,info.pos,info.len = string.find(val, "(%d+):(%d+)")
127 info.pos = string.format("%d:%02d", math.floor(info.pos / 60), math.mod(info.pos, 60))
128 info.len = string.format("%d:%02d", math.floor(info.len / 60), math.mod(info.len, 60))
129 elseif attrib == "state" then
130 info.state = val
132 until string.sub(data,1,2) == "OK" or string.sub(data,1,3) == "ACK"
133 if data == nil or string.sub(data,1,2) ~= "OK" then
134 mpd_socket:close()
135 return nil, "error querying mpd status"
138 -- 'currentsong'
139 -- song information
140 try(mpd_socket:send("currentsong\n"))
141 repeat
142 data = try(mpd_socket:receive())
143 if data == nil then break end
145 local _,_,attrib,val = string.find(data, "(.-): (.*)")
146 if attrib == "Artist" then info.artist = val
147 elseif attrib == "Title" then info.title = val
148 elseif attrib == "Album" then info.album = val
149 elseif attrib == "Track" then info.num = val
150 elseif attrib == "Date" then info.year = val
152 until string.sub(data,1,2) == "OK" or string.sub(data,1,3) == "ACK"
153 if data == nil or string.sub(data,1,2) ~= "OK" then
154 mpd_socket:close()
155 return nil, "error querying current song"
157 -- %artist_or_album
158 if info.artist == nil then
159 info.artist_or_album = info.album
160 else
161 info.artist_or_album = info.artist
164 success = true
166 -- done querying; now build the string
167 if info.state == "play" then
168 local mpd_st = settings.template
169 -- fill in %values
170 mpd_st = string.gsub(mpd_st, "%%([%w%_]+)", function (x) return(info[x] or "") end)
171 mpd_st = string.gsub(mpd_st, "%%%%", "%%")
172 return success, mpd_st
173 elseif info.state == "pause" then
174 return success, "Paused"
175 else
176 return success, "No song playing"
178 end)
180 local init_mpd -- forward declaration
182 local function update_mpd()
183 -- update unless there's an error that's not yet twice in a row, to allow
184 -- for transient errors due to load spikes
185 local success, mpd_st = get_mpd_status()
186 if success or not last_success then
187 statusd.inform("mpd", mpd_st)
190 if not success and not last_success then
191 -- something's wrong, try to reopen the connection
192 init_mpd()
193 else
194 mpd_timer:set(settings.update_interval, update_mpd)
197 last_success = success
200 init_mpd = function ()
201 -- Open the socket
202 --statusd.inform("mpd","Opening connection...")
203 success,errstr = open_socket()
204 if not success then
205 statusd.inform("mpd",errstr)
206 -- go to sleep for a while, then try again
207 mpd_timer:set(settings.retry_interval, init_mpd)
208 else
209 update_mpd()
213 -- Initialize
215 -- Sending a template we don't honor just confuses the statusbar
216 --send_template()
218 -- Go to sleep immediately, so a slow connect doesn't break the whole statusbar
219 mpd_timer=statusd.create_timer()
220 mpd_timer:set(2000,init_mpd)
221 statusd.inform("mpd", "Initializing")