5cbf0fac3304e25659d26a6a3cef7ab12bb2ec7c
[mpd-web.git] / system / mpd_class.php~
1 <?php\r
2 /*\r
3  *  mpd.class.php - PHP Object Interface to the MPD Music Player Daemon\r
4  *  Version 1.2, Released 05/05/2004\r
5  *  Copyright (C) 2003-2004  Benjamin Carlisle (bcarlisle@24oz.com)\r
6  *  http://mpd.24oz.com/ | http://www.musicpd.org/\r
7  *\r
8  *  This program is free software; you can redistribute it and/or modify\r
9  *  it under the terms of the GNU General Public License as published by\r
10  *  the Free Software Foundation; either version 2 of the License, or\r
11  *  (at your option) any later version.\r
12  *\r
13  *  This program is distributed in the hope that it will be useful,\r
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of\r
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
16  *  GNU General Public License for more details.\r
17  *\r
18  *  You should have received a copy of the GNU General Public License\r
19  *  along with this program; if not, write to the Free Software\r
20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
21  */ \r
22 \r
23 // Create common command definitions for MPD to use\r
24 define("MPD_CMD_STATUS",      "status");\r
25 define("MPD_CMD_STATISTICS",  "stats");\r
26 define("MPD_CMD_VOLUME",      "volume");\r
27 define("MPD_CMD_SETVOL",      "setvol");\r
28 define("MPD_CMD_PLAY",        "play");\r
29 define("MPD_CMD_STOP",        "stop");\r
30 define("MPD_CMD_PAUSE",       "pause");\r
31 define("MPD_CMD_NEXT",        "next");\r
32 define("MPD_CMD_PREV",        "previous");\r
33 define("MPD_CMD_PLLIST",      "playlistinfo");\r
34 define("MPD_CMD_PLADD",       "add");\r
35 define("MPD_CMD_PLREMOVE",    "delete");\r
36 define("MPD_CMD_PLCLEAR",     "clear");\r
37 define("MPD_CMD_PLSHUFFLE",   "shuffle");\r
38 define("MPD_CMD_PLLOAD",      "load");\r
39 define("MPD_CMD_PLSAVE",      "save");\r
40 define("MPD_CMD_KILL",        "kill");\r
41 define("MPD_CMD_REFRESH",     "update");\r
42 define("MPD_CMD_REPEAT",      "repeat");\r
43 define("MPD_CMD_LSDIR",       "lsinfo");\r
44 define("MPD_CMD_SEARCH",      "search");\r
45 define("MPD_CMD_START_BULK",  "command_list_begin");\r
46 define("MPD_CMD_END_BULK",    "command_list_end");\r
47 define("MPD_CMD_FIND",        "find");\r
48 define("MPD_CMD_RANDOM",      "random");\r
49 define("MPD_CMD_SEEK",        "seek");\r
50 define("MPD_CMD_PLSWAPTRACK", "swap");\r
51 define("MPD_CMD_PLMOVETRACK", "move");\r
52 define("MPD_CMD_PASSWORD",    "password");\r
53 define("MPD_CMD_TABLE",       "list");\r
54 define("MPD_CMD_LISTS",       "listplaylists");\r
55 \r
56 // Predefined MPD Response messages\r
57 define("MPD_RESPONSE_ERR", "ACK");\r
58 define("MPD_RESPONSE_OK",  "OK");\r
59 \r
60 // MPD State Constants\r
61 define("MPD_STATE_PLAYING", "play");\r
62 define("MPD_STATE_STOPPED", "stop");\r
63 define("MPD_STATE_PAUSED",  "pause");\r
64 \r
65 // MPD Searching Constants\r
66 define("MPD_SEARCH_ARTIST", "artist");\r
67 define("MPD_SEARCH_TITLE",  "title");\r
68 define("MPD_SEARCH_ALBUM",  "album");\r
69 \r
70 // MPD Cache Tables\r
71 define("MPD_TBL_ARTIST","artist");\r
72 define("MPD_TBL_ALBUM","album");\r
73 \r
74 class mpd {\r
75         // TCP/Connection variables\r
76         var $host;\r
77         var $port;\r
78     var $password;\r
79 \r
80         var $mpd_sock   = NULL;\r
81         var $connected  = FALSE;\r
82 \r
83         // MPD Status variables\r
84         var $mpd_version    = "(unknown)";\r
85 \r
86         var $state;\r
87         var $current_track_position;\r
88         var $current_track_length;\r
89         var $current_track_id;\r
90         var $volume;\r
91         var $repeat;\r
92         var $random;\r
93 \r
94         var $uptime;\r
95         var $playtime;\r
96         var $db_last_refreshed;\r
97         var $num_songs_played;\r
98         var $playlist_count;\r
99         \r
100         var $num_artists;\r
101         var $num_albums;\r
102         var $num_songs;\r
103         \r
104         var $playlist           = array();\r
105 \r
106         // Misc Other Vars      \r
107         var $mpd_class_version = "1.2";\r
108 \r
109         var $debugging   = TRUE;    // Set to TRUE to turn extended debugging on.\r
110         var $errStr      = "";       // Used for maintaining information about the last error message\r
111 \r
112         var $command_queue;          // The list of commands for bulk command sending\r
113 \r
114     // =================== BEGIN OBJECT METHODS ================\r
115 \r
116         /* mpd() : Constructor\r
117          * \r
118          * Builds the MPD object, connects to the server, and refreshes all local object properties.\r
119          */\r
120         function mpd($srv,$port,$pwd = NULL) {\r
121                 $this->host = $srv;\r
122                 $this->port = $port;\r
123         $this->password = $pwd;\r
124 \r
125                 $resp = $this->Connect();\r
126                 if ( is_null($resp) ) {\r
127             $this->errStr = "Could not connect";\r
128                         return;\r
129                 } else {\r
130                         list ( $this->mpd_version ) = sscanf($resp, MPD_RESPONSE_OK . " MPD %s\n");\r
131             if ( ! is_null($pwd) ) {\r
132                 if ( is_null($this->SendCommand(MPD_CMD_PASSWORD,$pwd)) ) {\r
133                     $this->connected = FALSE;\r
134                     return;  // bad password or command\r
135                 }\r
136                         if ( is_null($this->RefreshInfo()) ) { // no read access -- might as well be disconnected!\r
137                     $this->connected = FALSE;\r
138                     $this->errStr = "Password supplied does not have read access";\r
139                     return;\r
140                 }\r
141             } else {\r
142                         if ( is_null($this->RefreshInfo()) ) { // no read access -- might as well be disconnected!\r
143                     $this->connected = FALSE;\r
144                     $this->errStr = "Password required to access server";\r
145                     return; \r
146                 }\r
147             }\r
148                 }\r
149         }\r
150 \r
151         /* Connect()\r
152          * \r
153          * Connects to the MPD server. \r
154      * \r
155          * NOTE: This is called automatically upon object instantiation; you should not need to call this directly.\r
156          */\r
157         function Connect() {\r
158                 if ( $this->debugging ) echo "mpd->Connect() / host: ".$this->host.", port: ".$this->port."\n";\r
159                 $this->mpd_sock = fsockopen($this->host,$this->port,$errNo,$errStr,10);\r
160                 if (!$this->mpd_sock) {\r
161                         $this->errStr = "Socket Error: $errStr ($errNo)";\r
162                         return NULL;\r
163                 } else {\r
164                         while(!feof($this->mpd_sock)) {\r
165                                 $response =  fgets($this->mpd_sock,1024);\r
166                                 if (strncmp(MPD_RESPONSE_OK,$response,strlen(MPD_RESPONSE_OK)) == 0) {\r
167                                         $this->connected = TRUE;\r
168                                         return $response;\r
169                                         break;\r
170                                 }\r
171                                 if (strncmp(MPD_RESPONSE_ERR,$response,strlen(MPD_RESPONSE_ERR)) == 0) {\r
172                                         $this->errStr = "Server responded with: $response";\r
173                                         return NULL;\r
174                                 }\r
175                         }\r
176                         // Generic response\r
177                         $this->errStr = "Connection not available";\r
178                         return NULL;\r
179                 }\r
180         }\r
181 \r
182         /* SendCommand()\r
183          * \r
184          * Sends a generic command to the MPD server. Several command constants are pre-defined for \r
185          * use (see MPD_CMD_* constant definitions above). \r
186          */\r
187         function SendCommand($cmdStr,$arg1 = "",$arg2 = "") {\r
188                 if ( $this->debugging ) echo "mpd->SendCommand() / cmd: ".$cmdStr.", args: ".$arg1." ".$arg2."\n";\r
189                 \r
190                 if ( ! $this->connected ) {\r
191                         echo "mpd->SendCommand() / Error: Not connected\n";\r
192                 } else {\r
193                         // Clear out the error String\r
194                         $this->errStr = "";\r
195                         $respStr = "";\r
196 \r
197                         // Check the command compatibility:\r
198                         if ( ! $this->_checkCompatibility($cmdStr) ) {\r
199                                 echo "Not compatible command!";\r
200                                 return NULL;\r
201                         }\r
202 \r
203                         if (strlen($arg1) > 0) $cmdStr .= " \"$arg1\"";\r
204 \r
205                         if (strlen($arg2) > 0) $cmdStr .= " \"$arg2\"";\r
206                         if ( $this->debugging ) echo "mpd-> ".$cmdStr."\n";\r
207                         fputs($this->mpd_sock,"$cmdStr\n");\r
208                         while(!feof($this->mpd_sock)) {\r
209                                 $response = fgets($this->mpd_sock,1024);\r
210                                 if ( $this->debugging ) echo "mpd.response-> ".$response."\n";\r
211 \r
212                                 // An OK signals the end of transmission -- we'll ignore it\r
213                                 if (strncmp(MPD_RESPONSE_OK,$response,strlen(MPD_RESPONSE_OK)) == 0) {\r
214                                         break;\r
215                                 }\r
216 \r
217                                 // An ERR signals the end of transmission with an error! Let's grab the single-line message.\r
218                                 if (strncmp(MPD_RESPONSE_ERR,$response,strlen(MPD_RESPONSE_ERR)) == 0) {\r
219                                         list ( $junk, $errTmp ) = explode(MPD_RESPONSE_ERR . " ",$response );\r
220                                         $this->errStr = strtok($errTmp,"\n");\r
221                                 }\r
222 \r
223                                 if ( strlen($this->errStr) > 0 ) {\r
224                                         return NULL;\r
225                                 }\r
226 \r
227                                 // Build the response string\r
228                                 $respStr .= $response;\r
229                         }\r
230                         if ( $this->debugging ) echo "mpd->SendCommand() / response: '".$respStr."'\n";\r
231                 }\r
232                 return $respStr;\r
233         }\r
234 \r
235         /* QueueCommand() \r
236          *\r
237          * Queues a generic command for later sending to the MPD server. The CommandQueue can hold \r
238          * as many commands as needed, and are sent all at once, in the order they are queued, using \r
239          * the SendCommandQueue() method. The syntax for queueing commands is identical to SendCommand(). \r
240      */\r
241         function QueueCommand($cmdStr,$arg1 = "",$arg2 = "") {\r
242                 if ( $this->debugging ) echo "mpd->QueueCommand() / cmd: ".$cmdStr.", args: ".$arg1." ".$arg2."\n";\r
243                 if ( ! $this->connected ) {\r
244                         echo "mpd->QueueCommand() / Error: Not connected\n";\r
245                         return NULL;\r
246                 } else {\r
247                         if ( strlen($this->command_queue) == 0 ) {\r
248                                 $this->command_queue = MPD_CMD_START_BULK . "\n";\r
249                         }\r
250                         if (strlen($arg1) > 0) $cmdStr .= " \"$arg1\"";\r
251                         if (strlen($arg2) > 0) $cmdStr .= " \"$arg2\"";\r
252 \r
253                         $this->command_queue .= $cmdStr ."\n";\r
254 \r
255                         if ( $this->debugging ) echo "mpd->QueueCommand() / return\n";\r
256                 }\r
257                 return TRUE;\r
258         }\r
259 \r
260         /* SendCommandQueue() \r
261          *\r
262          * Sends all commands in the Command Queue to the MPD server. See also QueueCommand().\r
263      */\r
264         function SendCommandQueue() {\r
265                 if ( $this->debugging ) echo "mpd->SendCommandQueue()\n";\r
266                 if ( ! $this->connected ) {\r
267                         echo "mpd->SendCommandQueue() / Error: Not connected\n";\r
268                         return NULL;\r
269                 } else {\r
270                         $this->command_queue .= MPD_CMD_END_BULK . "\n";\r
271                         if ( is_null($respStr = $this->SendCommand($this->command_queue)) ) {\r
272                                 return NULL;\r
273                         } else {\r
274                                 $this->command_queue = NULL;\r
275                                 if ( $this->debugging ) echo "mpd->SendCommandQueue() / response: '".$respStr."'\n";\r
276                         }\r
277                 }\r
278                 return $respStr;\r
279         }\r
280 \r
281         /* AdjustVolume() \r
282          *\r
283          * Adjusts the mixer volume on the MPD by <modifier>, which can be a positive (volume increase),\r
284          * or negative (volume decrease) value. \r
285      */\r
286         function AdjustVolume($modifier) {\r
287                 if ( $this->debugging ) echo "mpd->AdjustVolume()\n";\r
288                 if ( ! is_numeric($modifier) ) {\r
289                         $this->errStr = "AdjustVolume() : argument 1 must be a numeric value";\r
290                         return NULL;\r
291                 }\r
292 \r
293         $this->RefreshInfo();\r
294         $newVol = $this->volume + $modifier;\r
295         $ret = $this->SetVolume($newVol);\r
296 \r
297                 if ( $this->debugging ) echo "mpd->AdjustVolume() / return\n";\r
298                 return $ret;\r
299         }\r
300 \r
301         /* SetVolume() \r
302          *\r
303          * Sets the mixer volume to <newVol>, which should be between 1 - 100.\r
304      */\r
305         function SetVolume($newVol) {\r
306                 if ( $this->debugging ) echo "mpd->SetVolume()\n";\r
307                 if ( ! is_numeric($newVol) ) {\r
308                         $this->errStr = "SetVolume() : argument 1 must be a numeric value";\r
309                         return NULL;\r
310                 }\r
311 \r
312         // Forcibly prevent out of range errors\r
313                 if ( $newVol < 0 )   $newVol = 0;\r
314                 if ( $newVol > 100 ) $newVol = 100;\r
315 \r
316         // If we're not compatible with SETVOL, we'll try adjusting using VOLUME\r
317         if ( $this->_checkCompatibility(MPD_CMD_SETVOL) ) {\r
318             if ( ! is_null($ret = $this->SendCommand(MPD_CMD_SETVOL,$newVol))) $this->volume = $newVol;\r
319         } else {\r
320                 $this->RefreshInfo();     // Get the latest volume\r
321                 if ( is_null($this->volume) ) {\r
322                         return NULL;\r
323                 } else {\r
324                         $modifier = ( $newVol - $this->volume );\r
325                 if ( ! is_null($ret = $this->SendCommand(MPD_CMD_VOLUME,$modifier))) $this->volume = $newVol;\r
326                 }\r
327         }\r
328 \r
329                 if ( $this->debugging ) echo "mpd->SetVolume() / return\n";\r
330                 return $ret;\r
331         }\r
332 \r
333         /* GetDir() \r
334          * \r
335      * Retrieves a database directory listing of the <dir> directory and places the results into\r
336          * a multidimensional array. If no directory is specified, the directory listing is at the \r
337          * base of the MPD music path. \r
338          */\r
339         function GetDir($dir = "") {\r
340                 if ( $this->debugging ) echo "mpd->GetDir()\n";\r
341                 $resp = $this->SendCommand(MPD_CMD_LSDIR,$dir);\r
342                 $dirlist = $this->_parseFileListResponse($resp);\r
343                 if ( $this->debugging ) echo "mpd->GetDir() / return ".print_r($dirlist)."\n";\r
344                 return $dirlist;\r
345         }\r
346 \r
347         /* PLAdd() \r
348          * \r
349      * Adds each track listed in a single-dimensional <trackArray>, which contains filenames \r
350          * of tracks to add, to the end of the playlist. This is used to add many, many tracks to \r
351          * the playlist in one swoop.\r
352          */\r
353         function PLAddBulk($trackArray) {\r
354                 if ( $this->debugging ) echo "mpd->PLAddBulk()\n";\r
355                 $num_files = count($trackArray);\r
356                 for ( $i = 0; $i < $num_files; $i++ ) {\r
357                         $this->QueueCommand(MPD_CMD_PLADD,$trackArray[$i]);\r
358                 }\r
359                 $resp = $this->SendCommandQueue();\r
360                 $this->RefreshInfo();\r
361                 if ( $this->debugging ) echo "mpd->PLAddBulk() / return\n";\r
362                 return $resp;\r
363         }\r
364 \r
365         /* PLAdd() \r
366          * \r
367          * Adds the file <file> to the end of the playlist. <file> must be a track in the MPD database. \r
368          */\r
369         function PLAdd($fileName) {\r
370                 if ( $this->debugging ) echo "mpd->PLAdd()\n";\r
371                 if ( ! is_null($resp = $this->SendCommand(MPD_CMD_PLADD,$fileName))) $this->RefreshInfo();\r
372                 if ( $this->debugging ) echo "mpd->PLAdd() / return\n";\r
373                 return $resp;\r
374         }\r
375 \r
376         /* PLMoveTrack() \r
377          * \r
378          * Moves track number <origPos> to position <newPos> in the playlist. This is used to reorder \r
379          * the songs in the playlist.\r
380          */\r
381         function PLMoveTrack($origPos, $newPos) {\r
382                 if ( $this->debugging ) echo "mpd->PLMoveTrack()\n";\r
383                 if ( ! is_numeric($origPos) ) {\r
384                         $this->errStr = "PLMoveTrack(): argument 1 must be numeric";\r
385                         return NULL;\r
386                 } \r
387                 if ( $origPos < 0 or $origPos > $this->playlist_count ) {\r
388                         $this->errStr = "PLMoveTrack(): argument 1 out of range";\r
389                         return NULL;\r
390                 }\r
391                 if ( $newPos < 0 ) $newPos = 0;\r
392                 if ( $newPos > $this->playlist_count ) $newPos = $this->playlist_count;\r
393                 \r
394                 if ( ! is_null($resp = $this->SendCommand(MPD_CMD_PLMOVETRACK,$origPos,$newPos))) $this->RefreshInfo();\r
395                 if ( $this->debugging ) echo "mpd->PLMoveTrack() / return\n";\r
396                 return $resp;\r
397         }\r
398 \r
399         /* PLShuffle() \r
400          * \r
401          * Randomly reorders the songs in the playlist.\r
402          */\r
403         function PLShuffle() {\r
404                 if ( $this->debugging ) echo "mpd->PLShuffle()\n";\r
405                 if ( ! is_null($resp = $this->SendCommand(MPD_CMD_PLSHUFFLE))) $this->RefreshInfo();\r
406                 if ( $this->debugging ) echo "mpd->PLShuffle() / return\n";\r
407                 return $resp;\r
408         }\r
409 \r
410         /* PLLoad() \r
411          * \r
412          * Retrieves the playlist from <file>.m3u and loads it into the current playlist. \r
413          */\r
414         function PLLoad($file) {\r
415                 if ( $this->debugging ) echo "mpd->PLLoad()\n";\r
416                 if ( ! is_null($resp = $this->SendCommand(MPD_CMD_PLLOAD,$file))) $this->RefreshInfo();\r
417                 if ( $this->debugging ) echo "mpd->PLLoad() / return\n";\r
418                 return $resp;\r
419         }\r
420 \r
421         /* PLList() \r
422          * \r
423          * Retrieves the playlists info. \r
424          */\r
425         function PLList() {\r
426                 if ( $this->debugging ) echo "mpd->PLList()\n";\r
427                 if ( ! is_null($resp = $this->SendCommand(MPD_CMD_LISTS))) $this->RefreshInfo();\r
428                 if ( $this->debugging ) echo "mpd->PLList() / return\n";\r
429                 return $resp;\r
430         }\r
431 \r
432         /* PLSave() \r
433          * \r
434          * Saves the playlist to <file>.m3u for later retrieval. The file is saved in the MPD playlist\r
435          * directory.\r
436          */\r
437         function PLSave($file) {\r
438                 if ( $this->debugging ) echo "mpd->PLSave()\n";\r
439                 $resp = $this->SendCommand(MPD_CMD_PLSAVE,$file);\r
440                 if ( $this->debugging ) echo "mpd->PLSave() / return\n";\r
441                 return $resp;\r
442         }\r
443 \r
444         /* PLClear() \r
445          * \r
446          * Empties the playlist.\r
447          */\r
448         function PLClear() {\r
449                 if ( $this->debugging ) echo "mpd->PLClear()\n";\r
450                 if ( ! is_null($resp = $this->SendCommand(MPD_CMD_PLCLEAR))) $this->RefreshInfo();\r
451                 if ( $this->debugging ) echo "mpd->PLClear() / return\n";\r
452                 return $resp;\r
453         }\r
454 \r
455 \r
456         /* PLMove()\r
457         *\r
458         * Move song from pos to the other\r
459         */\r
460         function Move ($from,$to) {\r
461                 if ( ! is_null($resp = $this->SendCommand(MPD_CMD_PLMOVETRACK,$from,$to))) $this->RefreshInfo();\r
462                 //die(print_r($resp));\r
463 \r
464         }\r
465 \r
466 \r
467         /* PLRemove() \r
468          * \r
469          * Removes track <id> from the playlist.\r
470          */\r
471         function PLRemove($id) {\r
472                 if ( $this->debugging ) echo "mpd->PLRemove()\n";\r
473                 if ( ! is_numeric($id) ) {\r
474                         $this->errStr = "PLRemove() : argument 1 must be a numeric value";\r
475                         return NULL;\r
476                 }\r
477                 if ( ! is_null($resp = $this->SendCommand(MPD_CMD_PLREMOVE,$id))) $this->RefreshInfo();\r
478                 if ( $this->debugging ) echo "mpd->PLRemove() / return\n";\r
479                 return $resp;\r
480         }\r
481 \r
482         /* SetRepeat() \r
483          * \r
484          * Enables 'loop' mode -- tells MPD continually loop the playlist. The <repVal> parameter \r
485          * is either 1 (on) or 0 (off).\r
486          */\r
487         function SetRepeat($repVal) {\r
488                 if ( $this->debugging ) echo "mpd->SetRepeat()\n";\r
489                 $rpt = $this->SendCommand(MPD_CMD_REPEAT,$repVal);\r
490                 $this->repeat = $repVal;\r
491                 if ( $this->debugging ) echo "mpd->SetRepeat() / return\n";\r
492                 return $rpt;\r
493         }\r
494 \r
495         /* SetRandom() \r
496          * \r
497          * Enables 'randomize' mode -- tells MPD to play songs in the playlist in random order. The\r
498          * <rndVal> parameter is either 1 (on) or 0 (off).\r
499          */\r
500         function SetRandom($rndVal) {\r
501                 if ( $this->debugging ) echo "mpd->SetRandom()\n";\r
502                 $resp = $this->SendCommand(MPD_CMD_RANDOM,$rndVal);\r
503                 $this->random = $rndVal;\r
504                 if ( $this->debugging ) echo "mpd->SetRandom() / return\n";\r
505                 return $resp;\r
506         }\r
507 \r
508         /* Shutdown() \r
509          * \r
510          * Shuts down the MPD server (aka sends the KILL command). This closes the current connection, \r
511          * and prevents future communication with the server. \r
512          */\r
513         function Shutdown() {\r
514                 if ( $this->debugging ) echo "mpd->Shutdown()\n";\r
515                 $resp = $this->SendCommand(MPD_CMD_SHUTDOWN);\r
516 \r
517                 $this->connected = FALSE;\r
518                 unset($this->mpd_version);\r
519                 unset($this->errStr);\r
520                 unset($this->mpd_sock);\r
521 \r
522                 if ( $this->debugging ) echo "mpd->Shutdown() / return\n";\r
523                 return $resp;\r
524         }\r
525 \r
526         /* DBRefresh() \r
527          * \r
528          * Tells MPD to rescan the music directory for new tracks, and to refresh the Database. Tracks \r
529          * cannot be played unless they are in the MPD database.\r
530          */\r
531         function DBRefresh() {\r
532                 if ( $this->debugging ) echo "mpd->DBRefresh()\n";\r
533                 $resp = $this->SendCommand(MPD_CMD_REFRESH);\r
534                 \r
535                 // Update local variables\r
536                 $this->RefreshInfo();\r
537 \r
538                 if ( $this->debugging ) echo "mpd->DBRefresh() / return\n";\r
539                 return $resp;\r
540         }\r
541 \r
542         /* Play() \r
543          * \r
544          * Begins playing the songs in the MPD playlist. \r
545          */\r
546         function Play() {\r
547                 if ( $this->debugging ) echo "mpd->Play()\n";\r
548                 if ( ! is_null($rpt = $this->SendCommand(MPD_CMD_PLAY) )) $this->RefreshInfo();\r
549                 if ( $this->debugging ) echo "mpd->Play() / return\n";\r
550                 return $rpt;\r
551         }\r
552 \r
553         /* Stop() \r
554          * \r
555          * Stops playing the MPD. \r
556          */\r
557         function Stop() {\r
558                 if ( $this->debugging ) echo "mpd->Stop()\n";\r
559                 if ( ! is_null($rpt = $this->SendCommand(MPD_CMD_STOP) )) $this->RefreshInfo();\r
560                 if ( $this->debugging ) echo "mpd->Stop() / return\n";\r
561                 return $rpt;\r
562         }\r
563 \r
564         /* Pause() \r
565          * \r
566          * Toggles pausing on the MPD. Calling it once will pause the player, calling it again\r
567          * will unpause. \r
568          */\r
569         function Pause() {\r
570                 if ( $this->debugging ) echo "mpd->Pause()\n";\r
571                 if ( ! is_null($rpt = $this->SendCommand(MPD_CMD_PAUSE) )) $this->RefreshInfo();\r
572                 if ( $this->debugging ) echo "mpd->Pause() / return\n";\r
573                 return $rpt;\r
574         }\r
575         \r
576         /* SeekTo() \r
577          * \r
578          * Skips directly to the <idx> song in the MPD playlist. \r
579          */\r
580         function SkipTo($idx) { \r
581                 if ( $this->debugging ) echo "mpd->SkipTo()\n";\r
582                 if ( ! is_numeric($idx) ) {\r
583                         $this->errStr = "SkipTo() : argument 1 must be a numeric value";\r
584                         return NULL;\r
585                 }\r
586                 if ( ! is_null($rpt = $this->SendCommand(MPD_CMD_PLAY,$idx))) $this->RefreshInfo();\r
587                 if ( $this->debugging ) echo "mpd->SkipTo() / return\n";\r
588                 return $idx;\r
589         }\r
590 \r
591         /* SeekTo() \r
592          * \r
593          * Skips directly to a given position within a track in the MPD playlist. The <pos> argument,\r
594          * given in seconds, is the track position to locate. The <track> argument, if supplied is\r
595          * the track number in the playlist. If <track> is not specified, the current track is assumed.\r
596          */\r
597         function SeekTo($pos, $track = -1) { \r
598                 if ( $this->debugging ) echo "mpd->SeekTo()\n";\r
599                 if ( ! is_numeric($pos) ) {\r
600                         $this->errStr = "SeekTo() : argument 1 must be a numeric value";\r
601                         return NULL;\r
602                 }\r
603                 if ( ! is_numeric($track) ) {\r
604                         $this->errStr = "SeekTo() : argument 2 must be a numeric value";\r
605                         return NULL;\r
606                 }\r
607                 if ( $track == -1 ) { \r
608                         $track = $this->current_track_id;\r
609                 } \r
610                 \r
611                 if ( ! is_null($rpt = $this->SendCommand(MPD_CMD_SEEK,$track,$pos))) $this->RefreshInfo();\r
612                 if ( $this->debugging ) echo "mpd->SeekTo() / return\n";\r
613                 return $pos;\r
614         }\r
615 \r
616         /* Next() \r
617          * \r
618          * Skips to the next song in the MPD playlist. If not playing, returns an error. \r
619          */\r
620         function Next() {\r
621                 if ( $this->debugging ) echo "mpd->Next()\n";\r
622                 if ( ! is_null($rpt = $this->SendCommand(MPD_CMD_NEXT))) $this->RefreshInfo();\r
623                 if ( $this->debugging ) echo "mpd->Next() / return\n";\r
624                 return $rpt;\r
625         }\r
626 \r
627         /* Previous() \r
628          * \r
629          * Skips to the previous song in the MPD playlist. If not playing, returns an error. \r
630          */\r
631         function Previous() {\r
632                 if ( $this->debugging ) echo "mpd->Previous()\n";\r
633                 if ( ! is_null($rpt = $this->SendCommand(MPD_CMD_PREV))) $this->RefreshInfo();\r
634                 if ( $this->debugging ) echo "mpd->Previous() / return\n";\r
635                 return $rpt;\r
636         }\r
637         \r
638         /* Search() \r
639          * \r
640      * Searches the MPD database. The search <type> should be one of the following: \r
641      *        MPD_SEARCH_ARTIST, MPD_SEARCH_TITLE, MPD_SEARCH_ALBUM\r
642      * The search <string> is a case-insensitive locator string. Anything that contains \r
643          * <string> will be returned in the results. \r
644          */\r
645         function Search($type,$string) {\r
646                 if ( $this->debugging ) echo "mpd->Search()\n";\r
647                 if ( $type != MPD_SEARCH_ARTIST and\r
648                  $type != MPD_SEARCH_ALBUM and\r
649                          $type != MPD_SEARCH_TITLE ) {\r
650                         $this->errStr = "mpd->Search(): invalid search type";\r
651                         return NULL;\r
652                 } else {\r
653                         if ( is_null($resp = $this->SendCommand(MPD_CMD_SEARCH,$type,$string))) return NULL;\r
654                         $searchlist = $this->_parseFileListResponse($resp);\r
655                 }\r
656                 if ( $this->debugging ) echo "mpd->Search() / return ".print_r($searchlist)."\n";\r
657                 return $searchlist;\r
658         }\r
659 \r
660         /* Find() \r
661          * \r
662          * Find() looks for exact matches in the MPD database. The find <type> should be one of \r
663          * the following: \r
664      *         MPD_SEARCH_ARTIST, MPD_SEARCH_TITLE, MPD_SEARCH_ALBUM\r
665      * The find <string> is a case-insensitive locator string. Anything that exactly matches \r
666          * <string> will be returned in the results. \r
667          */\r
668         function Find($type,$string) {\r
669                 if ( $this->debugging ) echo "mpd->Find()\n";\r
670                 if ( $type != MPD_SEARCH_ARTIST and\r
671                  $type != MPD_SEARCH_ALBUM and\r
672                          $type != MPD_SEARCH_TITLE ) {\r
673                         $this->errStr = "mpd->Find(): invalid find type";\r
674                         return NULL;\r
675                 } else {\r
676                         if ( is_null($resp = $this->SendCommand(MPD_CMD_FIND,$type,$string)))   return NULL;\r
677                         $searchlist = $this->_parseFileListResponse($resp);\r
678                 }\r
679                 if ( $this->debugging ) echo "mpd->Find() / return ".print_r($searchlist)."\n";\r
680                 return $searchlist;\r
681         }\r
682 \r
683         /* Disconnect() \r
684          * \r
685          * Closes the connection to the MPD server.\r
686          */\r
687         function Disconnect() {\r
688                 if ( $this->debugging ) echo "mpd->Disconnect()\n";\r
689                 fclose($this->mpd_sock);\r
690 \r
691                 $this->connected = FALSE;\r
692                 unset($this->mpd_version);\r
693                 unset($this->errStr);\r
694                 unset($this->mpd_sock);\r
695         }\r
696 \r
697         /* GetArtists() \r
698          * \r
699          * Returns the list of artists in the database in an associative array.\r
700         */\r
701         function GetArtists() {\r
702                 if ( $this->debugging ) echo "mpd->GetArtists()\n";\r
703                 if ( is_null($resp = $this->SendCommand(MPD_CMD_TABLE, MPD_TBL_ARTIST)))        return NULL;\r
704         $arArray = array();\r
705         \r
706         $arLine = strtok($resp,"\n");\r
707         $arName = "";\r
708         $arCounter = -1;\r
709         while ( $arLine ) {\r
710             list ( $element, $value ) = explode(": ",$arLine);\r
711             if ( $element == "Artist" ) {\r
712                 $arCounter++;\r
713                 $arName = $value;\r
714                 $arArray[$arCounter] = $arName;\r
715             }\r
716 \r
717             $arLine = strtok("\n");\r
718         }\r
719                 if ( $this->debugging ) echo "mpd->GetArtists()\n";\r
720         return $arArray;\r
721     }\r
722 \r
723     /* GetAlbums() \r
724          * \r
725          * Returns the list of albums in the database in an associative array. Optional parameter\r
726      * is an artist Name which will list all albums by a particular artist.\r
727         */\r
728         function GetAlbums( $ar = NULL) {\r
729                 if ( $this->debugging ) echo "mpd->GetAlbums()\n";\r
730                 if ( is_null($resp = $this->SendCommand(MPD_CMD_TABLE, MPD_TBL_ALBUM, $ar )))   return NULL;\r
731         $alArray = array();\r
732 \r
733         $alLine = strtok($resp,"\n");\r
734         $alName = "";\r
735         $alCounter = -1;\r
736         while ( $alLine ) {\r
737             list ( $element, $value ) = explode(": ",$alLine);\r
738             if ( $element == "Album" ) {\r
739                 $alCounter++;\r
740                 $alName = $value;\r
741                 $alArray[$alCounter] = $alName;\r
742             }\r
743 \r
744             $alLine = strtok("\n");\r
745         }\r
746                 if ( $this->debugging ) echo "mpd->GetAlbums()\n";\r
747         return $alArray;\r
748     }\r
749 \r
750         //*******************************************************************************//\r
751         //***************************** INTERNAL FUNCTIONS ******************************//\r
752         //*******************************************************************************//\r
753 \r
754     /* _computeVersionValue()\r
755      *\r
756      * Computes a compatibility value from a version string\r
757      *\r
758      */\r
759     function _computeVersionValue($verStr) {\r
760                 list ($ver_maj, $ver_min, $ver_rel ) = explode("\.",$verStr);\r
761                 return ( 100 * $ver_maj ) + ( 10 * $ver_min ) + ( $ver_rel );\r
762     }\r
763 \r
764         /* _checkCompatibility() \r
765          * \r
766          * Check MPD command compatibility against our internal table. If there is no version \r
767          * listed in the table, allow it by default.\r
768         */\r
769         function _checkCompatibility($cmd) {\r
770         // Check minimum compatibility\r
771                 $req_ver_low = $this->COMPATIBILITY_MIN_TBL[$cmd];\r
772                 $req_ver_hi = $this->COMPATIBILITY_MAX_TBL[$cmd];\r
773 \r
774                 $mpd_ver = $this->_computeVersionValue($this->mpd_version);\r
775 \r
776                 if ( $req_ver_low ) {\r
777                         $req_ver = $this->_computeVersionValue($req_ver_low);\r
778 \r
779                         if ( $mpd_ver < $req_ver ) {\r
780                                 $this->errStr = "Command '$cmd' is not compatible with this version of MPD, version ".$req_ver_low." required";\r
781                                 return FALSE;\r
782                         }\r
783                 }\r
784 \r
785         // Check maxmum compatibility -- this will check for deprecations\r
786                 if ( $req_ver_hi ) {\r
787             $req_ver = $this->_computeVersionValue($req_ver_hi);\r
788 \r
789                         if ( $mpd_ver > $req_ver ) {\r
790                                 $this->errStr = "Command '$cmd' has been deprecated in this version of MPD.";\r
791                                 return FALSE;\r
792                         }\r
793                 }\r
794 \r
795                 return TRUE;\r
796         }\r
797 \r
798         /* _parseFileListResponse() \r
799          * \r
800          * Builds a multidimensional array with MPD response lists.\r
801      *\r
802          * NOTE: This function is used internally within the class. It should not be used.\r
803          */\r
804         function _parseFileListResponse($resp) {\r
805                 if ( is_null($resp) ) {\r
806                         return NULL;\r
807                 } else {\r
808                         $plistArray = array();\r
809                         $plistLine = strtok($resp,"\n");\r
810                         $plistFile = "";\r
811                         $plCounter = -1;\r
812                         while ( $plistLine ) {\r
813                                 list ( $element, $value ) = explode(": ",$plistLine);\r
814                                 if($element == "file" || $element=="directory") {\r
815                                   $plCounter++;\r
816                                   $plistArray[$plCounter]['name']=$value;\r
817                                   $plistArray[$plCounter]['type']=$element; \r
818                                   $plistArray[$plCounter]['title']=$value;\r
819                                 }\r
820                                 if($element == "Name") {\r
821                                   $plistArray[$plCounter]['title']=$value;\r
822                                 }\r
823                                 $plistLine = strtok("\n");\r
824                         } \r
825                 }\r
826                 return $plistArray;\r
827         }\r
828 \r
829         /* _parsePlayListsResponse() \r
830          * \r
831          * Builds a multidimensional array with MPD response lists.\r
832      *\r
833          * NOTE: This function is used internally within the class. It should not be used.\r
834          */\r
835         function _parsePlayListsResponse($resp) {\r
836                 if ( is_null($resp) ) {\r
837                         return NULL;\r
838                 } else {\r
839                         $plistArray = array();\r
840                         $plistLine = strtok($resp,"\n");\r
841                         $plistFile = "";\r
842                         $plCounter = -1;\r
843                         while ( $plistLine ) {\r
844                                 list ( $element, $value ) = explode(": ",$plistLine);\r
845                                 if($element == "playlist") {\r
846                                   $plCounter++;\r
847                                   $plistArray[$plCounter]['name']=$value;\r
848                                 }\r
849                                 if($element == "Last-Modified") {\r
850                                   $plistArray[$plCounter]['timestamp']=$value;\r
851                                 }\r
852                                 $plistLine = strtok("\n");\r
853                         } \r
854                 }\r
855                 return $plistArray;\r
856         }\r
857 \r
858         /* RefreshInfo() \r
859          * \r
860          * Updates all class properties with the values from the MPD server.\r
861      *\r
862          * NOTE: This function is automatically called upon Connect() as of v1.1.\r
863          */\r
864         function RefreshInfo() {\r
865         // Get the Server Statistics\r
866                 $statStr = $this->SendCommand(MPD_CMD_STATISTICS);\r
867                 if ( !$statStr ) {\r
868                         return NULL;\r
869                 } else {\r
870                         $stats = array();\r
871                         $statLine = strtok($statStr,"\n");\r
872                         while ( $statLine ) {\r
873                                 list ( $element, $value ) = explode(": ",$statLine);\r
874                                 $stats[$element] = $value;\r
875                                 $statLine = strtok("\n");\r
876                         } \r
877                 }\r
878 \r
879         // Get the Server Status\r
880                 $statusStr = $this->SendCommand(MPD_CMD_STATUS);\r
881                 if ( ! $statusStr ) {\r
882                         return NULL;\r
883                 } else {\r
884                         $status = array();\r
885                         $statusLine = strtok($statusStr,"\n");\r
886                         while ( $statusLine ) {\r
887                                 list ( $element, $value ) = explode(": ",$statusLine);\r
888                                 $status[$element] = $value;\r
889                                 $statusLine = strtok("\n");\r
890                         }\r
891                 }\r
892 \r
893         // Get list of lists\r
894                 $plStr = $this->SendCommand(MPD_CMD_LISTS);\r
895                 $this->playlists = $this->_parsePlayListsResponse($plStr);\r
896 \r
897         // Get the Playlist\r
898                 $plStr = $this->SendCommand(MPD_CMD_PLLIST);\r
899                 $this->playlist = $this->_parseFileListResponse($plStr);\r
900                 $this->playlist_count = count($this->playlist);\r
901 \r
902         // Set Misc Other Variables\r
903                 $this->state = $status['state'];\r
904                 if ( ($this->state == MPD_STATE_PLAYING) || ($this->state == MPD_STATE_PAUSED) ) {\r
905                         $this->current_track_id = $status['song'];\r
906                         list ($this->current_track_position, $this->current_track_length ) = explode(":",$status['time']);\r
907                 } else {\r
908                         $this->current_track_id = -1;\r
909                         $this->current_track_position = -1;\r
910                         $this->current_track_length = -1;\r
911                 }\r
912 \r
913                 $this->repeat = $status['repeat'];\r
914                 $this->random = $status['random'];\r
915 \r
916                 $this->db_last_refreshed = $stats['db_update'];\r
917 \r
918                 $this->volume = $status['volume'];\r
919                 $this->uptime = $stats['uptime'];\r
920                 $this->playtime = $stats['playtime'];\r
921                 $this->num_songs_played = $stats['songs_played'];\r
922                 $this->num_artists = $stats['num_artists'];\r
923                 $this->num_songs = $stats['num_songs'];\r
924                 $this->num_albums = $stats['num_albums'];\r
925                 return TRUE;\r
926         }\r
927 \r
928     /* ------------------ DEPRECATED METHODS -------------------*/\r
929         /* GetStatistics() \r
930          * \r
931          * Retrieves the 'statistics' variables from the server and tosses them into an array.\r
932      *\r
933          * NOTE: This function really should not be used. Instead, use $this->[variable]. The function\r
934          *   will most likely be deprecated in future releases.\r
935          */\r
936         function GetStatistics() {\r
937                 if ( $this->debugging ) echo "mpd->GetStatistics()\n";\r
938                 $stats = $this->SendCommand(MPD_CMD_STATISTICS);\r
939                 if ( !$stats ) {\r
940                         return NULL;\r
941                 } else {\r
942                         $statsArray = array();\r
943                         $statsLine = strtok($stats,"\n");\r
944                         while ( $statsLine ) {\r
945                                 list ( $element, $value ) = explode(": ",$statsLine);\r
946                                 $statsArray[$element] = $value;\r
947                                 $statsLine = strtok("\n");\r
948                         } \r
949                 }\r
950                 if ( $this->debugging ) echo "mpd->GetStatistics() / return: " . print_r($statsArray) ."\n";\r
951                 return $statsArray;\r
952         }\r
953 \r
954         /* GetStatus() \r
955          * \r
956          * Retrieves the 'status' variables from the server and tosses them into an array.\r
957      *\r
958          * NOTE: This function really should not be used. Instead, use $this->[variable]. The function\r
959          *   will most likely be deprecated in future releases.\r
960          */\r
961         function GetStatus() {\r
962                 if ( $this->debugging ) echo "mpd->GetStatus()\n";\r
963                 $status = $this->SendCommand(MPD_CMD_STATUS);\r
964                 if ( ! $status ) {\r
965                         return NULL;\r
966                 } else {\r
967                         $statusArray = array();\r
968                         $statusLine = strtok($status,"\n");\r
969                         while ( $statusLine ) {\r
970                                 list ( $element, $value ) = explode(": ",$statusLine);\r
971                                 $statusArray[$element] = $value;\r
972                                 $statusLine = strtok("\n");\r
973                         }\r
974                 }\r
975                 if ( $this->debugging ) echo "mpd->GetStatus() / return: " . print_r($statusArray) ."\n";\r
976                 return $statusArray;\r
977         }\r
978 \r
979         /* GetVolume() \r
980          * \r
981          * Retrieves the mixer volume from the server.\r
982      *\r
983          * NOTE: This function really should not be used. Instead, use $this->volume. The function\r
984          *   will most likely be deprecated in future releases.\r
985          */\r
986         function GetVolume() {\r
987                 if ( $this->debugging ) echo "mpd->GetVolume()\n";\r
988                 $volLine = $this->SendCommand(MPD_CMD_STATUS);\r
989                 if ( ! $volLine ) {\r
990                         return NULL;\r
991                 } else {\r
992                         list ($vol) = sscanf($volLine,"volume: %d");\r
993                 }\r
994                 if ( $this->debugging ) echo "mpd->GetVolume() / return: $vol\n";\r
995                 return $vol;\r
996         }\r
997 \r
998         /* GetPlaylist() \r
999          * \r
1000          * Retrieves the playlist from the server and tosses it into a multidimensional array.\r
1001      *\r
1002          * NOTE: This function really should not be used. Instead, use $this->playlist. The function\r
1003          *   will most likely be deprecated in future releases.\r
1004          */\r
1005         function GetPlaylist() {\r
1006                 if ( $this->debugging ) echo "mpd->GetPlaylist()\n";\r
1007                 $resp = $this->SendCommand(MPD_CMD_PLLIST);\r
1008                 $playlist = $this->_parseFileListResponse($resp);\r
1009                 if ( $this->debugging ) echo "mpd->GetPlaylist() / return ".print_r($playlist)."\n";\r
1010                 return $playlist;\r
1011         }\r
1012 \r
1013     /* ----------------- Command compatibility tables --------------------- */\r
1014         var $COMPATIBILITY_MIN_TBL = array(\r
1015         );\r
1016 \r
1017     var $COMPATIBILITY_MAX_TBL = array(\r
1018     );\r
1019 \r
1020 }   // ---------------------------- end of class ------------------------------\r
1021 ?>\r