PageRenderTime 54ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/mpd/mpd.class.php

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