1 -- Authors: <delirium@hackish.org>
3 -- Last Changed: Unknown
5 -- statusd for MPD (Music Player Daemon)
6 -- bugs/requests/comments: delirium@hackish.org
8 -- requires that netcat is available in the path
11 -- 500 or less makes seconds increment relatively smoothly while playing
14 -- mpd server info (localhost:6600 are mpd defaults)
18 -- mpd password (if any)
23 -- can use the following:
24 -- track metadata: %artist, %title, %num, %album, %year, %len
25 -- current track position: %pos
26 -- escape for the percent character: %%
29 template
= "%artist - %num - %title (%pos / %len)"
32 local settings
=table.join(statusd
.get_config("mpd"), defaults
)
37 local function saferead(file
)
38 local data
, err
, errno
40 data
, err
, errno
= file
:read()
41 until errno
~= 4 -- EINTR
42 return data
, err
, errno
45 local function get_mpd_status()
47 -- The first version of cmd_string is the original, however, some versions
48 -- of MPD close the socket too quickly if close is included. If you see the
49 -- message: error querying mpd status
50 -- then try the second version.
51 local cmd_string
= "status\ncurrentsong\nclose\n"
52 -- local cmd_string = "status\ncurrentsong\n"
54 if settings
.password
~= nil then
55 cmd_string
= "password " .. settings
.password
.. "\n" .. cmd_string
57 cmd_string
= string.format('echo -n "%s" | netcat %s %d',
58 cmd_string
, settings
.address
, settings
.port
)
60 last_success
= success
63 local mpd
= io
.popen(cmd_string
, "r")
66 local data
= saferead(mpd
)
67 if data
== nil or string.sub(data
,1,6) ~= "OK MPD" then
69 return "mpd not running"
72 -- 'password' response (if necessary)
73 if settings
.password
~= nil then
76 until data
== nil or string.sub(data
,1,2) == "OK" or string.sub(data
,1,3) == "ACK"
77 if data
== nil or string.sub(data
,1,2) ~= "OK" then
79 return "bad mpd password"
88 if data
== nil then break end
90 local _
,_
,attrib
,val
= string.find(data
, "(.-): (.*)")
91 if attrib
== "time" then
92 _
,_
,info
.pos
,info
.len
= string.find(val
, "(%d+):(%d+)")
94 -- Around Lua 5.1, math.mod() was renamed math.fmod().
95 if type(math
.mod) == "function" then
96 info
.pos
= string.format("%d:%02d", math
.floor(info
.pos
/ 60), math
.mod(info
.pos
, 60))
97 info
.len
= string.format("%d:%02d", math
.floor(info
.len
/ 60), math
.mod(info
.len
, 60))
99 info
.pos
= string.format("%d:%02d", math
.floor(info
.pos
/ 60), math
.fmod(info
.pos
, 60))
100 info
.len
= string.format("%d:%02d", math
.floor(info
.len
/ 60), math
.fmod(info
.len
, 60))
102 elseif attrib
== "state" then
105 until string.sub(data
,1,2) == "OK" or string.sub(data
,1,3) == "ACK"
106 if data
== nil or string.sub(data
,1,2) ~= "OK" then
108 return "error querying mpd status"
111 -- 'currentsong' response
114 if data
== nil then break end
116 local _
,_
,attrib
,val
= string.find(data
, "(.-): (.*)")
117 if attrib
== "Artist" then info
.artist
= val
118 elseif attrib
== "Title" then info
.title
= val
119 elseif attrib
== "Album" then info
.album
= val
120 elseif attrib
== "Track" then info
.num
= val
121 elseif attrib
== "Date" then info
.year
= val
123 until string.sub(data
,1,2) == "OK" or string.sub(data
,1,3) == "ACK"
124 if data
== nil or string.sub(data
,1,2) ~= "OK" then
126 return "error querying current song"
133 -- done querying; now build the string
134 if info
.state
== "play" then
135 local mpd_st
= settings
.template
137 mpd_st
= string.gsub(mpd_st
, "%%([%w%_]+)", function (x
) return(info
[x
] or "") end)
138 mpd_st
= string.gsub(mpd_st
, "%%%%", "%%")
140 elseif info
.state
== "pause" then
143 return "No song playing"
150 local function update_mpd()
151 -- update unless there's an error that's not yet twice in a row, to allow
152 -- for transient errors due to load spikes
153 local mpd_st
= get_mpd_status()
154 if success
or not last_success
then
155 statusd
.inform("mpd", mpd_st
)
157 mpd_timer
:set(settings
.update_interval
, update_mpd
)
161 mpd_timer
=statusd
.create_timer()