Released version 3-2015061300
[notion.git] / contrib / statusd / statusd_mpd-socket.lua
blobf7852bbc14a32c0b6f6fcd37cb00b5855458fdd8
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+)")
128 -- Around Lua 5.1, math.mod() was renamed math.fmod().
129 if type(math.mod) == "function" then
130 info.pos = string.format("%d:%02d", math.floor(info.pos / 60), math.mod(info.pos, 60))
131 info.len = string.format("%d:%02d", math.floor(info.len / 60), math.mod(info.len, 60))
132 else
133 info.pos = string.format("%d:%02d", math.floor(info.pos / 60), math.fmod(info.pos, 60))
134 info.len = string.format("%d:%02d", math.floor(info.len / 60), math.fmod(info.len, 60))
136 elseif attrib == "state" then
137 info.state = val
139 until string.sub(data,1,2) == "OK" or string.sub(data,1,3) == "ACK"
140 if data == nil or string.sub(data,1,2) ~= "OK" then
141 mpd_socket:close()
142 return nil, "error querying mpd status"
145 -- 'currentsong'
146 -- song information
147 try(mpd_socket:send("currentsong\n"))
148 repeat
149 data = try(mpd_socket:receive())
150 if data == nil then break end
152 local _,_,attrib,val = string.find(data, "(.-): (.*)")
153 if attrib == "Artist" then info.artist = val
154 elseif attrib == "Title" then info.title = val
155 elseif attrib == "Album" then info.album = val
156 elseif attrib == "Track" then info.num = val
157 elseif attrib == "Date" then info.year = val
159 until string.sub(data,1,2) == "OK" or string.sub(data,1,3) == "ACK"
160 if data == nil or string.sub(data,1,2) ~= "OK" then
161 mpd_socket:close()
162 return nil, "error querying current song"
164 -- %artist_or_album
165 if info.artist == nil then
166 info.artist_or_album = info.album
167 else
168 info.artist_or_album = info.artist
171 success = true
173 -- done querying; now build the string
174 if info.state == "play" then
175 local mpd_st = settings.template
176 -- fill in %values
177 mpd_st = string.gsub(mpd_st, "%%([%w%_]+)", function (x) return(info[x] or "") end)
178 mpd_st = string.gsub(mpd_st, "%%%%", "%%")
179 return success, mpd_st
180 elseif info.state == "pause" then
181 return success, "Paused"
182 else
183 return success, "No song playing"
185 end)
187 local init_mpd -- forward declaration
189 local function update_mpd()
190 -- update unless there's an error that's not yet twice in a row, to allow
191 -- for transient errors due to load spikes
192 local success, mpd_st = get_mpd_status()
193 if success or not last_success then
194 statusd.inform("mpd", mpd_st)
197 if not success and not last_success then
198 -- something's wrong, try to reopen the connection
199 init_mpd()
200 else
201 mpd_timer:set(settings.update_interval, update_mpd)
204 last_success = success
207 init_mpd = function ()
208 -- Open the socket
209 --statusd.inform("mpd","Opening connection...")
210 success,errstr = open_socket()
211 if not success then
212 statusd.inform("mpd",errstr)
213 -- go to sleep for a while, then try again
214 mpd_timer:set(settings.retry_interval, init_mpd)
215 else
216 update_mpd()
220 -- Initialize
222 -- Sending a template we don't honor just confuses the statusbar
223 --send_template()
225 -- Go to sleep immediately, so a slow connect doesn't break the whole statusbar
226 mpd_timer=statusd.create_timer()
227 mpd_timer:set(2000,init_mpd)
228 statusd.inform("mpd", "Initializing")