]>
Commit | Line | Data |
---|---|---|
1 | // Written by Mike Frysinger <vapier@gmail.com>. Released into the public domain. Suck it. | |
2 | ||
3 | function Mpc(socket, cb_update_state, debug_enabled) { | |
4 | this._socket = socket; | |
5 | this._cb_update_state = cb_update_state; | |
6 | this._debug_enabled = debug_enabled; | |
7 | this._queue = ['init']; | |
8 | this.state = {}; | |
9 | } | |
10 | ||
11 | Mpc.prototype.log = function(msg, obj) { | |
12 | if (this._debug_enabled) | |
13 | console.log('mpc: ' + msg, obj); | |
14 | } | |
15 | ||
16 | Mpc.prototype.set_debug = function(val) { | |
17 | this._debug_enabled = val; | |
18 | } | |
19 | ||
20 | Mpc.prototype.send = function(msg) { | |
21 | var _this = this; | |
22 | this._queue.push(msg); | |
23 | this._socket.send(msg, function(x) { | |
24 | _this.log('send: ' + msg + ':', x); | |
25 | }); | |
26 | } | |
27 | ||
28 | Mpc.prototype.recv_msg = function(lines) { | |
29 | curr = this._queue.shift(); | |
30 | this.log('recv: [' + curr + ']:', lines.join('\n')); | |
31 | curr = curr.split(' '); | |
32 | ||
33 | switch (curr[0]) { | |
34 | // Needs to return a list of dicts (see above for dicts). | |
35 | //case 'playlistinfo': | |
36 | case 'currentsong': | |
37 | case 'stats': | |
38 | case 'status': | |
39 | state = {}; | |
40 | lines.forEach(function(line) { | |
41 | i = line.indexOf(':'); | |
42 | if (i == -1) | |
43 | return; // Ignores the OK line | |
44 | key = line.substr(0, i); | |
45 | val = line.substr(i + 2); | |
46 | state[key] = val; | |
47 | }); | |
48 | this.state = state; | |
49 | this._cb_update_state(state); | |
50 | break; | |
51 | default: | |
52 | this._cb_update_state(lines, curr); | |
53 | break; | |
54 | } | |
55 | } | |
56 | ||
57 | Mpc.prototype.recv = function(msg) { | |
58 | /* We can get back a bunch of responses in a row, so parse them out */ | |
59 | /* XXX: Do we have to handle partial reads ? like long playlists ... */ | |
60 | lines = msg.split('\n'); | |
61 | var i = 0; | |
62 | while (i < lines.length) { | |
63 | if (lines[i] == 'OK' || lines[i].substr(0, 3) == 'OK ') { | |
64 | this.recv_msg(lines.splice(0, i + 1)); | |
65 | i = 0; | |
66 | } else | |
67 | ++i; | |
68 | } | |
69 | } | |
70 | ||
71 | /* | |
72 | * Command generator helpers. | |
73 | */ | |
74 | ||
75 | Mpc.__make_send_void = function(cmd) { | |
76 | return function() { this.send(cmd); } | |
77 | } | |
78 | ||
79 | Mpc.__make_send_arg1 = function(cmd) { | |
80 | return function(a1) { | |
81 | if (a1 === undefined) | |
82 | this.log(cmd + ': function requires one argument'); | |
83 | else | |
84 | this.send(cmd + ' ' + a1); | |
85 | } | |
86 | } | |
87 | ||
88 | Mpc.__make_send_arg2 = function(cmd) { | |
89 | return function(a1, a2) { | |
90 | if (a1 === undefined || a2 === undefined) | |
91 | this.log(cmd + ': function requires two arguments'); | |
92 | else | |
93 | this.send(cmd + ' ' + a1 + ' ' + a2); | |
94 | } | |
95 | } | |
96 | ||
97 | Mpc.__make_send_opt = function(cmd) { | |
98 | return function(arg) { | |
99 | if (arg === undefined) | |
100 | arg = ''; | |
101 | this.send(cmd + ' ' + arg); | |
102 | }; | |
103 | } | |
104 | ||
105 | Mpc.__make_send_range = function(cmd, min, max, def) { | |
106 | return function(arg) { | |
107 | if (arg === undefined) | |
108 | arg = def; | |
109 | if (arg >= min && arg <= max) | |
110 | this.send(cmd + ' ' + arg); | |
111 | else | |
112 | this.log(cmd + ': arg must be [' + min + ',' + max + '] but got "' + arg + '"'); | |
113 | }; | |
114 | } | |
115 | ||
116 | /* | |
117 | * Querying MPD's status | |
118 | * http://www.musicpd.org/doc/protocol/ch03.html#idp118752 | |
119 | */ | |
120 | ||
121 | // clearerror | |
122 | Mpc.prototype.clearerror = Mpc.__make_send_void('clearerror'); | |
123 | // currentsong | |
124 | Mpc.prototype.currentsong = Mpc.__make_send_void('currentsong'); | |
125 | // idle [SUBSYSTEMS...] | |
126 | // TODO | |
127 | // status | |
128 | Mpc.prototype.status = Mpc.__make_send_void('status'); | |
129 | // stats | |
130 | Mpc.prototype.stats = Mpc.__make_send_void('stats'); | |
131 | ||
132 | /* | |
133 | * Playback options | |
134 | * http://www.musicpd.org/doc/protocol/ch03s02.html | |
135 | */ | |
136 | ||
137 | // consume {STATE} | |
138 | Mpc.prototype.consume = Mpc.__make_send_range('consume', 0, 1, 1); | |
139 | // crossfade {SECONDS} | |
140 | Mpc.prototype.crossfade = Mpc.__make_send_arg1('crossfade'); | |
141 | // mixrampdb {deciBels} | |
142 | Mpc.prototype.mixrampdb = Mpc.__make_send_arg1('mixrampdb'); | |
143 | // mixrampdelay {SECONDS|nan} | |
144 | // Note: Probably should handle javascript NaN here. | |
145 | Mpc.prototype.mixrampdelay = Mpc.__make_send_arg1('mixrampdelay'); | |
146 | // random {STATE} | |
147 | Mpc.prototype.random = Mpc.__make_send_range('random', 0, 1, 1); | |
148 | // repeat {STATE} | |
149 | Mpc.prototype.repeat = Mpc.__make_send_range('repeat', 0, 1, 1); | |
150 | // setvol {VOL} | |
151 | Mpc.prototype.setvol = Mpc.__make_send_range('setvol', 0, 100); | |
152 | // single {STATE} | |
153 | Mpc.prototype.single = Mpc.__make_send_range('single', 0, 1, 1); | |
154 | // replay_gain_mode {MODE} | |
155 | Mpc.prototype.replay_gain_mode = Mpc.__make_send_arg1('replay_gain_mode'); | |
156 | // replay_gain_status | |
157 | ||
158 | /* | |
159 | * Controlling playback | |
160 | * http://www.musicpd.org/doc/protocol/ch03s03.html | |
161 | */ | |
162 | ||
163 | // next | |
164 | Mpc.prototype.next = Mpc.__make_send_void('next'); | |
165 | // pause {PAUSE} | |
166 | Mpc.prototype.pause = Mpc.__make_send_range('pause', 0, 1, 1); | |
167 | // play [SONGPOS] | |
168 | Mpc.prototype.play = Mpc.__make_send_opt('play'); | |
169 | // playid [SONGID] | |
170 | Mpc.prototype.playid = Mpc.__make_send_opt('playid'); | |
171 | // previous | |
172 | Mpc.prototype.previous = Mpc.__make_send_void('previous'); | |
173 | // seek {SONGPOS} {TIME} | |
174 | Mpc.prototype.seek = Mpc.__make_send_arg2('seek'); | |
175 | // seekid {SONGID} {TIME} | |
176 | Mpc.prototype.seekid = Mpc.__make_send_arg2('seekid'); | |
177 | // seekcur {TIME} | |
178 | Mpc.prototype.seekcur = Mpc.__make_send_arg1('seek'); | |
179 | // stop | |
180 | Mpc.prototype.stop = Mpc.__make_send_void('stop'); | |
181 | ||
182 | /* | |
183 | * Connection settings | |
184 | * http://www.musicpd.org/doc/protocol/ch03s08.html | |
185 | */ | |
186 | ||
187 | // close | |
188 | Mpc.prototype.close = Mpc.__make_send_void('close'); | |
189 | // kill | |
190 | Mpc.prototype.kill = Mpc.__make_send_void('kill'); | |
191 | // password {PASSWORD} | |
192 | Mpc.prototype.password = Mpc.__make_send_arg1('password'); | |
193 | // ping | |
194 | Mpc.prototype.ping = Mpc.__make_send_void('ping'); | |
195 | ||
196 | /* | |
197 | * Audio output devices | |
198 | * http://www.musicpd.org/doc/protocol/ch03s09.html | |
199 | */ | |
200 | ||
201 | // disableoutput {ID} | |
202 | Mpc.prototype.disableoutput = Mpc.__make_send_arg1('disableoutput'); | |
203 | // enableoutput {ID} | |
204 | Mpc.prototype.enableoutput = Mpc.__make_send_arg1('enableoutput'); | |
205 | // outputs | |
206 | Mpc.prototype.outputs = Mpc.__make_send_void('outputs'); | |
207 | ||
208 | /* | |
209 | * Reflection | |
210 | * http://www.musicpd.org/doc/protocol/ch03s10.html | |
211 | */ | |
212 | ||
213 | // config | |
214 | Mpc.prototype.config = Mpc.__make_send_void('config'); | |
215 | // commands | |
216 | Mpc.prototype.commands = Mpc.__make_send_void('commands'); | |
217 | // notcommands | |
218 | Mpc.prototype.notcommands = Mpc.__make_send_void('notcommands'); | |
219 | // tagtypes | |
220 | Mpc.prototype.tagtypes = Mpc.__make_send_void('tagtypes'); | |
221 | // urlhandlers | |
222 | Mpc.prototype.urlhandlers = Mpc.__make_send_void('urlhandlers'); | |
223 | // decoders | |
224 | Mpc.prototype.decoders = Mpc.__make_send_void('decoders'); | |
225 | ||
226 | /* | |
227 | * Client to client | |
228 | * http://www.musicpd.org/doc/protocol/ch03s11.html | |
229 | */ | |
230 | ||
231 | // subscribe {NAME} | |
232 | Mpc.prototype.subscribe = Mpc.__make_send_arg1('subscribe'); | |
233 | // unsubscribe {NAME} | |
234 | Mpc.prototype.unsubscribe = Mpc.__make_send_arg1('unsubscribe'); | |
235 | // channels | |
236 | Mpc.prototype.channels = Mpc.__make_send_void('channels'); | |
237 | // readmessages | |
238 | Mpc.prototype.readmessages = Mpc.__make_send_void('readmessages'); | |
239 | // sendmessage {CHANNEL} {TEXT} | |
240 | Mpc.prototype.sendmessage = Mpc.__make_send_arg2('sendmessage'); |