|
1 |
|
2 -- Requires libsocket |
|
3 |
|
4 -- TODO: |
|
5 -- do pubsub tunes+notify instead of status hacking |
|
6 |
|
7 require 'socket' |
|
8 |
|
9 local settings = { |
|
10 hostname = "localhost", |
|
11 password = nil, |
|
12 port = 6600, |
|
13 } |
|
14 |
|
15 mpd = {} |
|
16 |
|
17 -- separator allows split output into records, that are started by any of present in separator keys |
|
18 -- returns table of field (lowercase) - value records |
|
19 -- command status is returned in STATUS field |
|
20 -- on unexpected errors returns just false, dropping any available data |
|
21 function mpd.receive_message ( tcp, separator ) |
|
22 local ret = {} |
|
23 local line = tcp:receive ( '*l' ) |
|
24 while line and not ( line:find ( '^OK' ) or line:find ( '^ACK' ) ) do |
|
25 local key, val = line:match ( '^(.-):%s*(.*)$' ) |
|
26 if key then |
|
27 if separator then |
|
28 key = key:lower () |
|
29 if separator[key] then |
|
30 table.insert ( ret, {} ) |
|
31 end |
|
32 ret[#ret][key] = val |
|
33 else |
|
34 ret[key:lower()] = val |
|
35 end |
|
36 end |
|
37 line = tcp:receive ( '*l' ) |
|
38 end |
|
39 if not line then |
|
40 return false -- an error occured, ret, most likely, does not contains exact and complete info... |
|
41 else |
|
42 ret.STATUS = line:match ( '^%S+' ) |
|
43 return ret |
|
44 end |
|
45 end |
|
46 |
|
47 -- use: mpd.call_command { 'status' } |
|
48 -- mpd.call_command { 'lsinfo misc', list = { file = true, directory = true } } |
|
49 -- mpd.call_command { 'next', noret = true } |
|
50 -- mpd.call_command { 'status', 'currentsong' } |
|
51 -- on one command returns just results of that command, on multi - array of results |
|
52 -- on errors returns nil |
|
53 -- noret can terminate socket too early, thus, do not use it with lists of commands |
|
54 function mpd.call_command ( opts ) |
|
55 local tcp = socket.tcp () |
|
56 if not tcp then |
|
57 print ( 'mpd: cannot get master tcp object' ) |
|
58 return nil |
|
59 elseif not tcp:connect ( settings.hostname, settings.port ) then |
|
60 tcp:close () |
|
61 print ( 'mpd: cannot connect to server' ) |
|
62 return nil |
|
63 end |
|
64 |
|
65 local ret = {} |
|
66 if not opts.noret then |
|
67 ret = mpd.receive_message ( tcp ) |
|
68 if not ret then |
|
69 tcp:close () |
|
70 print ( 'mpd: error getting greeting from server' ) |
|
71 return nil |
|
72 elseif ret.STATUS ~= 'OK' then |
|
73 print ( 'mpd: server ack\'s in greeting' ) |
|
74 end |
|
75 end |
|
76 |
|
77 for num, command in ipairs ( opts ) do |
|
78 if not tcp:send ( command .. "\n" ) then |
|
79 tcp:close () |
|
80 print ( 'mpd: error sending command ' .. command ) |
|
81 return nil |
|
82 end |
|
83 if not opts.noret then |
|
84 ret[num] = mpd.receive_message ( tcp, opts.list ) |
|
85 if not ret[num] then |
|
86 print ( 'mpd: error getting result' ) |
|
87 end |
|
88 if ret[num]['STATUS'] ~= 'OK' then |
|
89 print ( 'mpd: server acks our command ' .. command ) |
|
90 end |
|
91 end |
|
92 end |
|
93 |
|
94 tcp:close () |
|
95 if #ret > 1 then |
|
96 return ret |
|
97 else |
|
98 return ret[1] |
|
99 end |
|
100 end |
|
101 |
|
102 -- MCABBER PART -- |
|
103 |
|
104 mpd_enabled = false |
|
105 |
|
106 function mpd_getstatus () |
|
107 if not mpd_enabled then |
|
108 return '' |
|
109 end |
|
110 |
|
111 local stats = mpd.call_command { 'status', 'currentsong' } |
|
112 if stats[1].state ~= 'play' and stats[1].state ~= 'pause' then |
|
113 return '' |
|
114 end |
|
115 |
|
116 local title = stats[2].title |
|
117 if not title then |
|
118 if stats[2].file then |
|
119 title = stats[2].file |
|
120 else |
|
121 title = '' |
|
122 end |
|
123 elseif not stats[2].artist then |
|
124 title = string.format ( "%s (%s)", title, stats[2].file ) |
|
125 else |
|
126 title = string.format ( "%s - %s", stats[2].artist, title ) |
|
127 end |
|
128 |
|
129 if stats[1].state == 'pause' then |
|
130 return string.format ( "[mpd: <зупинено> %s]", title ) |
|
131 else |
|
132 return string.format ( "[mpd: %s]", title ) |
|
133 end |
|
134 end |
|
135 |
|
136 function parse_status () |
|
137 local stletter, stmessage = main.status () |
|
138 local cmd = char2status[stletter] |
|
139 local message, mpd_string = stmessage:match ( "^(.-)%s+(%[mpd:%s+.+%s*%])" ) |
|
140 if message then |
|
141 return cmd, message, mpd_string |
|
142 else |
|
143 return cmd, stmessage, '' |
|
144 end |
|
145 end |
|
146 |
|
147 function mpd_callback () |
|
148 local new_mpd_string = mpd_getstatus () |
|
149 local status, message, mpd_string = parse_status () |
|
150 if new_mpd_string ~= mpd_string then |
|
151 main.run ( string.format ( 'status %s %s %s', status, message, new_mpd_string ) ) |
|
152 end |
|
153 if mpd_enabled then |
|
154 return true |
|
155 else |
|
156 return false |
|
157 end |
|
158 end |
|
159 |
|
160 -- do not call it too fast, or you end up with many daemons at once |
|
161 function enable_mpd ( yn ) |
|
162 if yn == nil then |
|
163 yn = true |
|
164 end |
|
165 if yn then |
|
166 if not mpd_enabled then |
|
167 main.timer ( 15, mpd_callback ) |
|
168 mpd_enabled = true |
|
169 -- update status |
|
170 end |
|
171 else |
|
172 if mpd_enabled then |
|
173 mpd_enabled = false |
|
174 -- update status |
|
175 end |
|
176 end |
|
177 end |
|
178 |
|
179 main.add_command ( 'mpd', |
|
180 function ( args ) |
|
181 local enable = yesno ( args ) |
|
182 if enable == nil then |
|
183 if mpd_enabled then |
|
184 print ( "MPD status string is enabled" ) |
|
185 else |
|
186 print ( "MPD status string is disabled" ) |
|
187 end |
|
188 else |
|
189 enable_mpd ( enable ) |
|
190 end |
|
191 end ) |
|
192 |
|
193 commands_help['mpd'] = "[enable|disable|on|off|yes|no|true|false]\n\nSets state of mpd string in your status.\nIf state is omitted, prints current state." |
|
194 |
|
195 -- vim: se ts=4: -- |