PageRenderTime 62ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/jukebox/jukeboxes/mpd.php

https://github.com/jinzora/jinzora3
PHP | 1713 lines | 1096 code | 167 blank | 450 comment | 284 complexity | f38e4979beb7be10ab99c9737004297c MD5 | raw file
  1. <?php if (!defined(JZ_SECURE_ACCESS)) die ('Security breach detected.');
  2. /**
  3. * - JINZORA | Web-based Media Streamer -
  4. *
  5. * Jinzora is a Web-based media streamer, primarily desgined to stream MP3s
  6. * (but can be used for any media file that can stream from HTTP).
  7. * Jinzora can be integrated into a CMS site, run as a standalone application,
  8. * or integrated into any PHP website. It is released under the GNU GPL.
  9. *
  10. * - Resources -
  11. * - Jinzora Author: Ross Carlson <ross@jasbone.com>
  12. * - Web: http://www.jinzora.org
  13. * - Documentation: http://www.jinzora.org/docs
  14. * - Support: http://www.jinzora.org/forum
  15. * - Downloads: http://www.jinzora.org/downloads
  16. * - License: GNU GPL <http://www.gnu.org/copyleft/gpl.html>
  17. *
  18. * - Contributors -
  19. * Please see http://www.jinzora.org/team.html
  20. *
  21. * - Code Purpose -
  22. * Contains the Winamp httpQ plugin functions
  23. *
  24. * @since 2/9/05
  25. * @author Ross Carlson <ross@jinzora.org>
  26. */
  27. /*
  28. NOTES FOR THIS JUKEBOX
  29. This Jukebox requires the following settings:
  30. server
  31. port
  32. password
  33. description
  34. type
  35. An example would be:
  36. $jbArr[1]['server'] = "localhost";
  37. $jbArr[1]['port'] = "6600";
  38. $jbArr[1]['description'] = "MPD Player";
  39. $jbArr[1]['password'] = "PASS";
  40. $jbArr[1]['type'] = "mpd";
  41. // $jbArr[1]['prefix'] = "http"; // send weblinks to MPD.
  42. */
  43. /**
  44. * The installer function for this jukebox
  45. *
  46. * @author Ross Carlson
  47. * @version 11/20/05
  48. * @since 11/20/05
  49. * @param $step int The step of the install process we are on
  50. */
  51. function jbInstall($step){
  52. global $jbArr;
  53. echo "<strong>MPD Jukebox Installer</strong><br><br>";
  54. // Now which step are we on?
  55. switch($step){
  56. case "2":
  57. // Now let's create the step 2 page
  58. ?>
  59. Please complete the following to setup MPD with Jinzora.<br><br>
  60. <strong>NOTE:</strong> MPD must already be running and have imported the same media folder(s)<br>that Jinzora has in order for MPD to function properly with Jinzora.<br><br>
  61. <form method="post">
  62. <table>
  63. <tr>
  64. <td>
  65. Server:
  66. </td>
  67. <td>
  68. <input type="text" value="localhost" name="edit_server" class="jz_input">
  69. </td>
  70. </tr>
  71. <tr>
  72. <td>
  73. Port:
  74. </td>
  75. <td>
  76. <input type="text" value="6600" name="edit_port" class="jz_input">
  77. </td>
  78. </tr>
  79. <tr>
  80. <td>
  81. Password:
  82. </td>
  83. <td>
  84. <input type="password" value="" name="edit_password" class="jz_input"> (optional)
  85. </td>
  86. </tr>
  87. <tr>
  88. <td>
  89. Name:
  90. </td>
  91. <td>
  92. <input type="text" value="MPD Player" name="edit_description" class="jz_input">
  93. </td>
  94. </tr>
  95. <tr>
  96. <td></td>
  97. <td>
  98. <br>
  99. <input type="submit" value="Test Connection and Write Settings" name="edit_finish" class="jz_submit">
  100. <br><br>
  101. </td>
  102. </tr>
  103. </table>
  104. <input type="hidden" name="edit_step" value="3">
  105. <input type="hidden" name="edit_jukebox_type" value="mpd">
  106. </form>
  107. <?php
  108. exit();
  109. break;
  110. case "3":
  111. // Let's test the connection to the player
  112. // First let's set all the variables for it
  113. $jbArr[0]['server'] = $_POST['edit_server'];
  114. $jbArr[0]['port'] = $_POST['edit_port'];
  115. $jbArr[0]['description'] = $_POST['edit_description'];
  116. $jbArr[0]['password'] = $_POST['edit_password'];
  117. $jbArr[0]['type'] = "mpd";
  118. $_SESSION['jb_id'] = 0;
  119. echo "Testing connection to MPD...<br>";
  120. if (playerConnect()){
  121. echo word("Success! Please wait while we write the settings...")."<br><br>";
  122. flushdisplay();
  123. sleep(1);
  124. } else {
  125. echo word("Failed! Jinzora had an issue communicating with MPD, ensure that it's running and that you've specified the proper settings");
  126. exit();
  127. }
  128. // Ok, let's create the settings file
  129. $content = "<?". "php\n";
  130. $content .= "$". "jbArr[0]['server'] = '". $_POST['edit_server']. "';\n";
  131. $content .= "$". "jbArr[0]['port'] = '". $_POST['edit_port']. "';\n";
  132. $content .= "$". "jbArr[0]['description'] = '". $_POST['edit_description']. "';\n";
  133. $content .= "$". "jbArr[0]['password'] = '". $_POST['edit_password']. "';\n";
  134. $content .= "$". "jbArr[0]['type'] = 'mpd';\n";
  135. $content .= "?>";
  136. // Now let's write it out IF we can
  137. $filename = getcwd(). "/jukebox/settings.php";
  138. if (is_writable($filename)){
  139. $handle = fopen($filename, "w");
  140. fwrite($handle,$content);
  141. fclose ($handle);
  142. ?>
  143. <form method="post">
  144. <input type="submit" name="continue" value="Continue to the Jukebox interface" class="jz_submit"><br><br>
  145. </form>
  146. <?php
  147. exit();
  148. } else {
  149. echo 'It looks like your jukebox settings file at "'. $filename. '" is not writeable.<br>You must make it writeable to proceed!';
  150. echo '<br><br>If you are on Linux you can execute "chmod 666 '. $filename. '" at a shell to make it writeable.<br><br>';
  151. echo "Or copy and paste the below information into the file at: ". getcwd(). "/jukebox/settings.php<br><br>";
  152. echo str_replace(" ","&nbsp;&nbsp;&nbsp;&nbsp;",nl2br(str_replace("<?php", "&lt;php",$content)));
  153. echo "<br><br>";
  154. exit();
  155. }
  156. break;
  157. }
  158. }
  159. /**
  160. * Returns a connection to the player
  161. *
  162. * @author Martijn Pieters
  163. * @version 4/10/05
  164. * @since 4/10/05
  165. * @param return Returns mpd instance
  166. */
  167. function & _mpdConnection(){
  168. global $jbArr;
  169. if (isset($jbArr[$_SESSION['jb_id']]['password']) && $jbArr[$_SESSION['jb_id']]['password'] != "") {
  170. $r = &new mpd($jbArr[$_SESSION['jb_id']]['server'],$jbArr[$_SESSION['jb_id']]['port'],$jbArr[$_SESSION['jb_id']]['password']);
  171. return $r;
  172. } else {
  173. $r = &new mpd($jbArr[$_SESSION['jb_id']]['server'],$jbArr[$_SESSION['jb_id']]['port']);
  174. return $r;
  175. }
  176. }
  177. /**
  178. * Returns the stats of the jukebox
  179. *
  180. * @author Ross Carlson
  181. * @version 2/9/05
  182. * @since 2/9/05
  183. * @param return Returns a keyed array of the jukeboxe's abilities
  184. */
  185. function retJBStats(){
  186. global $jbArr;
  187. return;
  188. }
  189. /**
  190. * Returns a keyed array showing all the functions that this jukebox supports
  191. *
  192. * @author Ross Carlson
  193. * @version 2/9/05
  194. * @since 2/9/05
  195. * @param return Returns a keyed array of the jukeboxe's abilities
  196. */
  197. function returnJBAbilities(){
  198. $retArray['playbutton'] = true;
  199. $retArray['pausebutton'] = true;
  200. $retArray['stopbutton'] = true;
  201. $retArray['nextbutton'] = true;
  202. $retArray['prevbutton'] = true;
  203. $retArray['shufflebutton'] = true;
  204. $retArray['clearbutton'] = true;
  205. $retArray['repeatbutton'] = true;
  206. $retArray['delonebutton'] = true;
  207. $retArray['status'] = true;
  208. $retArray['progress'] = true;
  209. $retArray['volume'] = true;
  210. $retArray['addtype'] = true;
  211. $retArray['nowplaying'] = true;
  212. $retArray['nexttrack'] = true;
  213. $retArray['fullplaylist'] = true;
  214. $retArray['refreshtime'] = true;
  215. $retArray['jump'] = true;
  216. $retArray['stats'] = false;
  217. $retArray['move'] = true;
  218. return $retArray;
  219. }
  220. /**
  221. * Returns the connection status of the player true or false
  222. *
  223. * @author Ross Carlson
  224. * @version 2/9/05
  225. * @since 2/9/05
  226. * @param return Returns true or false
  227. */
  228. function playerConnect(){
  229. global $jbArr;
  230. $myMpd = _mpdConnection();
  231. if ($myMpd->state == ""){
  232. return false;
  233. } else {
  234. return true;
  235. }
  236. }
  237. /**
  238. * Returns Addon tools for MPD - namely refresh jukebox database
  239. *
  240. * @author Ross Carlson
  241. * @version 2/9/05
  242. * @since 2/9/05
  243. * @param return Returns a link to refresh the MPD database
  244. */
  245. function getAllAddOnTools(){
  246. $arr = array();
  247. $arr['action'] = "jukebox";
  248. $arr['subaction'] = "jukebox-command";
  249. $arr['command'] = "refreshdb";
  250. $arr['ptype'] = "jukebox";
  251. return ' - <a href="#" onClick="sendJukeboxRequest(\'refreshdb\')">refresh MPD</a>';
  252. }
  253. /**
  254. * Returns the currently playing tracks path so we can get the node
  255. *
  256. * @author Ross Carlson
  257. * @version 2/9/05
  258. * @since 2/9/05
  259. * @param return Returns the currently playling track's path
  260. */
  261. function getCurTrackPath(){
  262. global $jbArr,$media_dirs;
  263. $myMpd = _mpdConnection();
  264. if (isset($jbArr[$_SESSION['jb_id']]['prefix']) && $jbArr[$_SESSION['jb_id']]['prefix'] == "http") {
  265. $id = getTrackIdFromURL($myMpd->playlist[$myMpd->current_track_id]['file']);
  266. $track = new jzMediaTrack($id,"id");
  267. return $track->getFilename("server");
  268. }
  269. $num = getCurPlayingTrack();
  270. $pArray = $myMpd->GetPlaylist();
  271. if (false === stristr($media_dirs,"|")) {
  272. $base = $media_dirs . "/";
  273. } else {
  274. $base = "";
  275. }
  276. return $base . $pArray[$num]['file'];
  277. }
  278. /**
  279. * Returns the currently playing track number
  280. *
  281. * @author Ross Carlson
  282. * @version 2/9/05
  283. * @since 2/9/05
  284. * @param return Returns the currently playling track number
  285. */
  286. function getCurPlayingTrack(){
  287. global $jbArr;
  288. $myMpd = _mpdConnection();
  289. return $myMpd->current_track_id;
  290. }
  291. /**
  292. * Returns the currently playing playlist
  293. *
  294. * @author Ross Carlson
  295. * @version 2/9/05
  296. * @since 2/9/05
  297. * @param return Returns the currently playling playlist
  298. * @param bolean Return FULL path only?
  299. */
  300. function getCurPlaylist($path = false){
  301. global $jbArr;
  302. $myMpd = _mpdConnection();
  303. $retArray = array();
  304. if (!is_null($myMpd->playlist)){
  305. foreach ($myMpd->playlist as $id => $entry) {
  306. if ($path){
  307. $retArray[] = $entry['file'];
  308. } else {
  309. if (isset($jbArr[$_SESSION['jb_id']]['prefix']) && $jbArr[$_SESSION['jb_id']]['prefix'] == "http") {
  310. if (false !== ($id = getTrackIdFromURL($entry['file']))) {
  311. $track = new jzMediaTrack($id,'id');
  312. $meta = $track->getMeta();
  313. $retArray[] = $meta['artist'] . ' - ' . $meta['title'];
  314. } else {
  315. $retArray[] = word('Unknown');
  316. }
  317. } else {
  318. $retArray[] = $entry['Artist']." - ".$entry['Title'];
  319. }
  320. }
  321. }
  322. }
  323. return $retArray;
  324. }
  325. /**
  326. * Passes a playlist to the jukebox player
  327. *
  328. * @author Ross Carlson
  329. * @version 2/9/05
  330. * @since 2/9/05
  331. * @param $playlist The playlist that we are passing
  332. */
  333. function playlist($playlist){
  334. global $include_path, $jbArr, $media_dirs,$jzSERVICES;
  335. if (isset($jbArr[$_SESSION['jb_id']]['prefix']) && $jbArr[$_SESSION['jb_id']]['prefix'] == "http") {
  336. $content = "";
  337. foreach ($playlist->getList() as $track) {
  338. $content .= $track->getFileName("user")."\n";
  339. }
  340. $playlist = $content;
  341. } else {
  342. $playlist = $jzSERVICES->createPlaylist($playlist,"jukebox");
  343. }
  344. $myMpd = _mpdConnection();
  345. // Now let's get our current playlist and current position
  346. $curList = getCurPlaylist(true);
  347. $curTrack = getCurPlayingTrack();
  348. switch ($_SESSION['jb-addtype']) {
  349. case "end":
  350. case "current":
  351. $restart_playback = false;
  352. break;
  353. default:
  354. $restart_playback = true;
  355. }
  356. // Ok, now we need to figure out where to add the stuff
  357. if ($_SESSION['jb-addtype'] == "current"){
  358. // Ok, let's split our first playlist in 2 so we can add in the middle
  359. //$begArr = array_slice($curList,0,$curTrack+1);
  360. if (is_array($curList)) {
  361. $begArr = array();
  362. $endArr = array_slice($curList,$curTrack+1);
  363. } else {
  364. $begArr = array();
  365. $endArr = array();
  366. }
  367. } else if ($_SESSION['jb-addtype'] == "begin"){
  368. $begArr = array();
  369. $endArr = $curList;
  370. } else if ($_SESSION['jb-addtype'] == "end"){
  371. $begArr = array();
  372. $endArr = array();
  373. } else if ($_SESSION['jb-addtype'] == "replace") {
  374. $begArr = array();
  375. $endArr = array();
  376. }
  377. if ($restart_playback === true){
  378. $myMpd->Stop();
  379. $myMpd->PLClear();
  380. } else if ($_SESSION['jb-addtype'] == "current") {
  381. // Remove everything at the end of the playlist, since we are going to readd it all.
  382. for ($i = sizeof($curList); $i > $curTrack; $i--) {
  383. $myMpd->PLRemove($i);
  384. }
  385. }
  386. // Now let's send the new playlist to the player
  387. for ($i=0; $i < count($begArr); $i++){
  388. // Now let's add this
  389. if ($begArr[$i] <> ''){
  390. $myMpd->PLAdd($begArr[$i]);
  391. }
  392. }
  393. // Ok, Now let's add the new stuff
  394. $pArray = explode("\n",$playlist);
  395. for ($i=0; $i < count($pArray); $i++){
  396. if ($pArray[$i] <> ""){
  397. // Now let's clean up the paths so we can add the media
  398. $mArr = explode("|",$media_dirs);
  399. $track = trim($pArray[$i]);
  400. for ($e=0; $e < count($mArr); $e++){
  401. if (false !== strpos($track,$mArr[$e])) {
  402. $track = trim(str_replace($mArr[$e]. "/","",$track));
  403. if ($track[0] == '/') {
  404. $track = substr($track,1);
  405. }
  406. }
  407. }
  408. $myMpd->PLAdd($track);
  409. }
  410. }
  411. // Now let's finish this out
  412. for ($i=0; $i < count($endArr); $i++){
  413. // Now let's add this
  414. if ($endArr[$i] <> ''){
  415. $myMpd->PLAdd($endArr[$i]);
  416. }
  417. }
  418. // Now let's jump to where we need to play
  419. switch ($_SESSION['jb-addtype']){
  420. case "current":
  421. if ($curTrack == 0){$curTrack = -1;}
  422. $_POST['jbjumpto'] = $curTrack + 1;
  423. break;
  424. case "end":
  425. $_POST['jbjumpto'] = $curTrack;
  426. break;
  427. case "replace":
  428. case "begin":
  429. $_POST['jbjumpto'] = 0;
  430. break;
  431. }
  432. if ($restart_playback) {
  433. control("jumpto");
  434. }
  435. control("play");
  436. if (defined('NO_AJAX_JUKEBOX')) {
  437. ?>
  438. <script>
  439. history.back();
  440. </script>
  441. <?php
  442. }
  443. exit();
  444. }
  445. /**
  446. * Passes a command to the jukebox player
  447. *
  448. * @author Ross Carlson
  449. * @version 2/9/05
  450. * @since 2/9/05
  451. * @param $command The command that we passed to the player
  452. */
  453. function control($command){
  454. global $jbArr;
  455. $myMpd = _mpdConnection();
  456. // Now let's execute the command
  457. switch ($command){
  458. case "play":
  459. $myMpd->Play();
  460. break;
  461. case "stop":
  462. $myMpd->Stop();
  463. break;
  464. case "pause":
  465. $myMpd->Pause();
  466. break;
  467. case "previous":
  468. $myMpd->Previous();
  469. break;
  470. case "next":
  471. $myMpd->Next();
  472. break;
  473. case "volume":
  474. $myMpd->SetVolume($_POST['jbvol']);
  475. $_SESSION['jz_jbvol-'. $_SESSION['jb_id']] = $_POST['jbvol'];
  476. break;
  477. case "playwhere":
  478. // Ok, let's set where they are playing
  479. $_SESSION['jb_playwhere'] = $_POST['jbplaywhere'];
  480. // Now let's figure out it's ID
  481. for ($i=0; $i < count($jbArr); $i++){
  482. if ($jbArr[$i]['description'] == $_SESSION['jb_playwhere']){
  483. $_SESSION['jb_id'] = $i;
  484. }
  485. }
  486. break;
  487. case "jumpto":
  488. // We need to add 1 so we don't start at 0
  489. $myMpd->SkipTo($_POST['jbjumpto']);
  490. $_SESSION['jbSelectedItems'] = array($_POST['jbjumpto']);
  491. break;
  492. case "clear":
  493. $myMpd->Stop();
  494. $myMpd->PLClear();
  495. break;
  496. case "delone":
  497. for ( $i = sizeof($_POST['jbSelectedItems']) - 1; $i >= 0; $i--) {
  498. $myMpd->PLRemove($_POST['jbSelectedItems'][$i]);
  499. }
  500. $_SESSION['jbSelectedItems'] = array();
  501. break;
  502. case "repeat":
  503. $myMpd->setRepeat(1);
  504. break;
  505. case "no_repeat":
  506. $myMpd->setRepeat(0);
  507. break;
  508. case "random_play":
  509. $myMpd->Stop();
  510. $myMpd->PLShuffle();
  511. $myMpd->Play();
  512. break;
  513. case "refreshdb":
  514. $myMpd->DBRefresh();
  515. break;
  516. case "addwhere":
  517. $_SESSION['jb-addtype'] = $_POST['addplat'];
  518. break;
  519. case "moveup":
  520. $items = $_POST['jbSelectedItems'];
  521. sort($items);
  522. $i = 0;
  523. // find first moveable.
  524. while ($i < sizeof($items) && $items[$i] == $i) {
  525. $i++;
  526. }
  527. for ($i; $i < sizeof($items); $i++) {
  528. $myMpd->PLMoveTrack($items[$i],$items[$i]-1);
  529. // update for displaying the list:
  530. $items[$i] = $items[$i]-1;
  531. }
  532. $_SESSION['jbSelectedItems'] = $items;
  533. break;
  534. case "movedown":
  535. $items = $_POST['jbSelectedItems'];
  536. sort($items);
  537. $i = sizeof($items) - 1;
  538. $j = sizeof($myMpd->GetPlaylist()) - 1;
  539. // find first moveable.
  540. while ($i >= 0 && $items[$i] == $j) {
  541. $i--; $j--;
  542. }
  543. for ($i; $i >= 0; $i--) {
  544. $myMpd->PLMoveTrack($items[$i],$items[$i]+1);
  545. // update for displaying the list:
  546. $items[$i] = $items[$i]+1;
  547. }
  548. $_SESSION['jbSelectedItems'] = $items;
  549. break;
  550. }
  551. if (defined('NO_AJAX_JUKEBOX')) {
  552. ?>
  553. <script>
  554. history.back();
  555. </script>
  556. <?php
  557. }
  558. }
  559. /**
  560. * Returns the players current status
  561. * Can get the following statuses: playback|repeat
  562. *
  563. * @author Ross Carlson
  564. * @version 2/9/05
  565. * @since 2/9/05
  566. */
  567. function getStatus($type = "playback"){
  568. global $jbArr;
  569. $myMpd = _mpdConnection();
  570. switch ($type) {
  571. case "repeat":
  572. return ($myMpd->repeat) ? true : false;
  573. break;
  574. case "playback":
  575. switch ($myMpd->state) {
  576. case MPD_STATE_PLAYING:
  577. return "playing";
  578. break;
  579. case MPD_STATE_PAUSED:
  580. return "paused";
  581. break;
  582. case MPD_STATE_STOPPED:
  583. return "stopped";
  584. break;
  585. }
  586. break;
  587. }
  588. }
  589. /**
  590. * Returns the current playing track
  591. *
  592. * @author Ross Carlson
  593. * @version 2/9/05
  594. * @since 2/9/05
  595. * @return Returns the name of the current playing track
  596. */
  597. function getCurTrackName(){
  598. global $jbArr;
  599. $myMpd = _mpdConnection();
  600. if ($myMpd->current_track_id == -1) return false;
  601. if (isset($jbArr[$_SESSION['jb_id']]['prefix']) && $jbArr[$_SESSION['jb_id']]['prefix'] == "http") {
  602. $id = getTrackIdFromURL($myMpd->playlist[$myMpd->current_track_id]['file']);
  603. if (false !== $id) {
  604. $track = new jzMediaTrack($id,'id');
  605. $meta = $track->getMeta();
  606. return $meta['artist'] . ' - ' . $meta['title'];
  607. } else {
  608. return 'Unknown';
  609. }
  610. } else {
  611. return $myMpd->playlist[$myMpd->current_track_id]['Artist']." - ".$myMpd->playlist[$myMpd->current_track_id]['Title'];
  612. }
  613. }
  614. /**
  615. * Returns how long is left in the current track (in seconds)
  616. *
  617. * @author Ross Carlson
  618. * @version 2/9/05
  619. * @since 2/9/05
  620. * @return Returns the name of the current playing track
  621. */
  622. function getCurTrackRemaining(){
  623. global $jbArr;
  624. // Let's create our object
  625. $myMpd = _mpdConnection();
  626. // Let's return what's left
  627. return ($myMpd->current_track_length - getCurTrackLocation());
  628. }
  629. /**
  630. * Gets the length of the current track
  631. *
  632. * @author Ross Carlson
  633. * @version 2/9/05
  634. * @since 2/9/05
  635. * @param return returns the amount of time remaining in seconds
  636. */
  637. function getCurTrackLength(){
  638. global $jbArr;
  639. // Let's create our object
  640. $myMpd = _mpdConnection();
  641. return $myMpd->current_track_length;
  642. }
  643. /**
  644. * Returns how long is left in the current track (in seconds)
  645. *
  646. * @author Ross Carlson
  647. * @version 2/9/05
  648. * @since 2/9/05
  649. * @return Returns the name of the current playing track
  650. */
  651. function getCurTrackLocation(){
  652. global $jbArr;
  653. // Let's create our object
  654. $myMpd = _mpdConnection();
  655. return $myMpd->current_track_position;
  656. }
  657. /**
  658. * Updates the database
  659. *
  660. * @author Ben Dodson
  661. * @version 12/08/06
  662. * @since 12/08/06
  663. */
  664. function updateJukeboxDB($node, $recursive, $root_path){
  665. global $jbArr;
  666. // Let's create our object
  667. $myMpd = _mpdConnection();
  668. $myMpd->DBRefresh();
  669. }
  670. //
  671. //
  672. // The following code was obtained from http://mpd.24oz.com/download.html
  673. //
  674. //
  675. // Create common command definitions for MPD to use
  676. define("MPD_CMD_STATUS", "status");
  677. define("MPD_CMD_STATISTICS", "stats");
  678. define("MPD_CMD_VOLUME", "volume");
  679. define("MPD_CMD_SETVOL", "setvol");
  680. define("MPD_CMD_PLAY", "play");
  681. define("MPD_CMD_STOP", "stop");
  682. define("MPD_CMD_PAUSE", "pause");
  683. define("MPD_CMD_NEXT", "next");
  684. define("MPD_CMD_PREV", "previous");
  685. define("MPD_CMD_PLLIST", "playlistinfo");
  686. define("MPD_CMD_PLADD", "add");
  687. define("MPD_CMD_PLREMOVE", "delete");
  688. define("MPD_CMD_PLCLEAR", "clear");
  689. define("MPD_CMD_PLSHUFFLE", "shuffle");
  690. define("MPD_CMD_PLLOAD", "load");
  691. define("MPD_CMD_PLSAVE", "save");
  692. define("MPD_CMD_KILL", "kill");
  693. define("MPD_CMD_REFRESH", "update");
  694. define("MPD_CMD_REPEAT", "repeat");
  695. define("MPD_CMD_LSDIR", "lsinfo");
  696. define("MPD_CMD_SEARCH", "search");
  697. define("MPD_CMD_START_BULK", "command_list_begin");
  698. define("MPD_CMD_END_BULK", "command_list_end");
  699. define("MPD_CMD_FIND", "find");
  700. define("MPD_CMD_RANDOM", "random");
  701. define("MPD_CMD_SEEK", "seek");
  702. define("MPD_CMD_PLSWAPTRACK", "swap");
  703. define("MPD_CMD_PLMOVETRACK", "move");
  704. define("MPD_CMD_PLMOVE", "move"); // Don't know if this is right..
  705. define("MPD_CMD_PASSWORD", "password");
  706. define("MPD_CMD_TABLE", "list");
  707. // Predefined MPD Response messages
  708. define("MPD_RESPONSE_ERR", "ACK");
  709. define("MPD_RESPONSE_OK", "OK");
  710. // MPD State Constants
  711. define("MPD_STATE_PLAYING", "play");
  712. define("MPD_STATE_STOPPED", "stop");
  713. define("MPD_STATE_PAUSED", "pause");
  714. // MPD Searching Constants
  715. define("MPD_SEARCH_ARTIST", "artist");
  716. define("MPD_SEARCH_TITLE", "title");
  717. define("MPD_SEARCH_ALBUM", "album");
  718. // MPD Cache Tables
  719. define("MPD_TBL_ARTIST","artist");
  720. define("MPD_TBL_ALBUM","album");
  721. class mpd {
  722. // TCP/Connection variables
  723. var $host;
  724. var $port;
  725. var $password;
  726. var $mpd_sock = NULL;
  727. var $connected = FALSE;
  728. // MPD Status variables
  729. var $mpd_version = "(unknown)";
  730. var $state;
  731. var $current_track_position;
  732. var $current_track_length;
  733. var $current_track_id;
  734. var $volume;
  735. var $repeat;
  736. var $random;
  737. var $uptime;
  738. var $playtime;
  739. var $db_last_refreshed;
  740. var $num_songs_played;
  741. var $playlist_count;
  742. var $num_artists;
  743. var $num_albums;
  744. var $num_songs;
  745. var $playlist = array();
  746. // Misc Other Vars
  747. var $mpd_class_version = "1.2";
  748. var $debugging = FALSE; // Set to TRUE to turn extended debugging on.
  749. var $errStr = ""; // Used for maintaining information about the last error message
  750. var $command_queue; // The list of commands for bulk command sending
  751. // =================== BEGIN OBJECT METHODS ================
  752. /* mpd() : Constructor
  753. *
  754. * Builds the MPD object, connects to the server, and refreshes all local object properties.
  755. */
  756. function mpd($srv,$port,$pwd = NULL) {
  757. $this->host = $srv;
  758. $this->port = $port;
  759. $this->password = $pwd;
  760. $resp = $this->Connect();
  761. if ( is_null($resp) ) {
  762. $this->errStr = "Could not connect";
  763. return;
  764. } else {
  765. list ( $this->mpd_version ) = sscanf($resp, MPD_RESPONSE_OK . " MPD %s\n");
  766. if ( ! is_null($pwd) ) {
  767. if ( is_null($this->SendCommand(MPD_CMD_PASSWORD,$pwd)) ) {
  768. $this->connected = FALSE;
  769. return; // bad password or command
  770. }
  771. if ( is_null($this->RefreshInfo()) ) { // no read access -- might as well be disconnected!
  772. $this->connected = FALSE;
  773. $this->errStr = "Password supplied does not have read access";
  774. return;
  775. }
  776. } else {
  777. if ( is_null($this->RefreshInfo()) ) { // no read access -- might as well be disconnected!
  778. $this->connected = FALSE;
  779. $this->errStr = "Password required to access server";
  780. return;
  781. }
  782. }
  783. }
  784. }
  785. /* Connect()
  786. *
  787. * Connects to the MPD server.
  788. *
  789. * NOTE: This is called automatically upon object instantiation; you should not need to call this directly.
  790. */
  791. function Connect() {
  792. if ( $this->debugging ) echo "mpd->Connect() / host: ".$this->host.", port: ".$this->port."\n";
  793. $this->mpd_sock = fsockopen($this->host,$this->port,$errNo,$errStr,10);
  794. if (!$this->mpd_sock) {
  795. $this->errStr = "Socket Error: $errStr ($errNo)";
  796. return NULL;
  797. } else {
  798. while(!feof($this->mpd_sock)) {
  799. $response = fgets($this->mpd_sock,1024);
  800. if (strncmp(MPD_RESPONSE_OK,$response,strlen(MPD_RESPONSE_OK)) == 0) {
  801. $this->connected = TRUE;
  802. return $response;
  803. break;
  804. }
  805. if (strncmp(MPD_RESPONSE_ERR,$response,strlen(MPD_RESPONSE_ERR)) == 0) {
  806. $this->errStr = "Server responded with: $response";
  807. return NULL;
  808. }
  809. }
  810. // Generic response
  811. $this->errStr = "Connection not available";
  812. return NULL;
  813. }
  814. }
  815. /* SendCommand()
  816. *
  817. * Sends a generic command to the MPD server. Several command constants are pre-defined for
  818. * use (see MPD_CMD_* constant definitions above).
  819. */
  820. function SendCommand($cmdStr,$arg1 = "",$arg2 = "") {
  821. if ( $this->debugging ) echo "mpd->SendCommand() / cmd: ".$cmdStr.", args: ".$arg1." ".$arg2."\n";
  822. if ( ! $this->connected ) {
  823. echo "mpd->SendCommand() / Error: Not connected\n";
  824. } else {
  825. // Clear out the error String
  826. $this->errStr = "";
  827. $respStr = "";
  828. // Check the command compatibility:
  829. if ( ! $this->_checkCompatibility($cmdStr) ) {
  830. return NULL;
  831. }
  832. if (strlen($arg1) > 0) $cmdStr .= " \"".utf8_encode($arg1)."\"";
  833. if (strlen($arg2) > 0) $cmdStr .= " \"".utf8_encode($arg2)."\"";
  834. fputs($this->mpd_sock,"$cmdStr\n");
  835. while(!feof($this->mpd_sock)) {
  836. $response = fgets($this->mpd_sock,1024);
  837. // An OK signals the end of transmission -- we'll ignore it
  838. if (strncmp(MPD_RESPONSE_OK,$response,strlen(MPD_RESPONSE_OK)) == 0) {
  839. break;
  840. }
  841. // An ERR signals the end of transmission with an error! Let's grab the single-line message.
  842. if (strncmp(MPD_RESPONSE_ERR,$response,strlen(MPD_RESPONSE_ERR)) == 0) {
  843. list ( $junk, $errTmp ) = split(MPD_RESPONSE_ERR . " ",$response );
  844. $this->errStr = strtok($errTmp,"\n");
  845. }
  846. if ( strlen($this->errStr) > 0 ) {
  847. return NULL;
  848. }
  849. // Build the response string
  850. $respStr .= $response;
  851. }
  852. if ( $this->debugging ) echo "mpd->SendCommand() / response: '".$respStr."'\n";
  853. }
  854. return $respStr;
  855. }
  856. /* QueueCommand()
  857. *
  858. * Queues a generic command for later sending to the MPD server. The CommandQueue can hold
  859. * as many commands as needed, and are sent all at once, in the order they are queued, using
  860. * the SendCommandQueue() method. The syntax for queueing commands is identical to SendCommand().
  861. */
  862. function QueueCommand($cmdStr,$arg1 = "",$arg2 = "") {
  863. if ( $this->debugging ) echo "mpd->QueueCommand() / cmd: ".$cmdStr.", args: ".$arg1." ".$arg2."\n";
  864. if ( ! $this->connected ) {
  865. echo "mpd->QueueCommand() / Error: Not connected\n";
  866. return NULL;
  867. } else {
  868. if ( strlen($this->command_queue) == 0 ) {
  869. $this->command_queue = MPD_CMD_START_BULK . "\n";
  870. }
  871. if (strlen($arg1) > 0) $cmdStr .= " \"" . utf8_encode($arg1) . "\"";
  872. if (strlen($arg2) > 0) $cmdStr .= " \"" . utf8_encode($arg2) . "\"";
  873. $this->command_queue .= $cmdStr ."\n";
  874. if ( $this->debugging ) echo "mpd->QueueCommand() / return\n";
  875. }
  876. return TRUE;
  877. }
  878. /* SendCommandQueue()
  879. *
  880. * Sends all commands in the Command Queue to the MPD server. See also QueueCommand().
  881. */
  882. function SendCommandQueue() {
  883. if ( $this->debugging ) echo "mpd->SendCommandQueue()\n";
  884. if ( ! $this->connected ) {
  885. echo "mpd->SendCommandQueue() / Error: Not connected\n";
  886. return NULL;
  887. } else {
  888. $this->command_queue .= MPD_CMD_END_BULK . "\n";
  889. if ( is_null($respStr = $this->SendCommand($this->command_queue)) ) {
  890. return NULL;
  891. } else {
  892. $this->command_queue = NULL;
  893. if ( $this->debugging ) echo "mpd->SendCommandQueue() / response: '".$respStr."'\n";
  894. }
  895. }
  896. return $respStr;
  897. }
  898. /* AdjustVolume()
  899. *
  900. * Adjusts the mixer volume on the MPD by <modifier>, which can be a positive (volume increase),
  901. * or negative (volume decrease) value.
  902. */
  903. function AdjustVolume($modifier) {
  904. if ( $this->debugging ) echo "mpd->AdjustVolume()\n";
  905. if ( ! is_numeric($modifier) ) {
  906. $this->errStr = "AdjustVolume() : argument 1 must be a numeric value";
  907. return NULL;
  908. }
  909. $this->RefreshInfo();
  910. $newVol = $this->volume + $modifier;
  911. $ret = $this->SetVolume($newVol);
  912. if ( $this->debugging ) echo "mpd->AdjustVolume() / return\n";
  913. return $ret;
  914. }
  915. /* SetVolume()
  916. *
  917. * Sets the mixer volume to <newVol>, which should be between 1 - 100.
  918. */
  919. function SetVolume($newVol) {
  920. if ( $this->debugging ) echo "mpd->SetVolume()\n";
  921. if ( ! is_numeric($newVol) ) {
  922. $this->errStr = "SetVolume() : argument 1 must be a numeric value";
  923. return NULL;
  924. }
  925. // Forcibly prevent out of range errors
  926. if ( $newVol < 0 ) $newVol = 0;
  927. if ( $newVol > 100 ) $newVol = 100;
  928. // If we're not compatible with SETVOL, we'll try adjusting using VOLUME
  929. if ( $this->_checkCompatibility(MPD_CMD_SETVOL) ) {
  930. if ( ! is_null($ret = $this->SendCommand(MPD_CMD_SETVOL,$newVol))) $this->volume = $newVol;
  931. } else {
  932. $this->RefreshInfo(); // Get the latest volume
  933. if ( is_null($this->volume) ) {
  934. return NULL;
  935. } else {
  936. $modifier = ( $newVol - $this->volume );
  937. if ( ! is_null($ret = $this->SendCommand(MPD_CMD_VOLUME,$modifier))) $this->volume = $newVol;
  938. }
  939. }
  940. if ( $this->debugging ) echo "mpd->SetVolume() / return\n";
  941. return $ret;
  942. }
  943. /* GetDir()
  944. *
  945. * Retrieves a database directory listing of the <dir> directory and places the results into
  946. * a multidimensional array. If no directory is specified, the directory listing is at the
  947. * base of the MPD music path.
  948. */
  949. function GetDir($dir = "") {
  950. if ( $this->debugging ) echo "mpd->GetDir()\n";
  951. $resp = $this->SendCommand(MPD_CMD_LSDIR,$dir);
  952. $dirlist = $this->_parseFileListResponse($resp);
  953. if ( $this->debugging ) echo "mpd->GetDir() / return ".print_r($dirlist)."\n";
  954. return $dirlist;
  955. }
  956. /* PLAdd()
  957. *
  958. * Adds each track listed in a single-dimensional <trackArray>, which contains filenames
  959. * of tracks to add, to the end of the playlist. This is used to add many, many tracks to
  960. * the playlist in one swoop.
  961. */
  962. function PLAddBulk($trackArray) {
  963. if ( $this->debugging ) echo "mpd->PLAddBulk()\n";
  964. $num_files = count($trackArray);
  965. for ( $i = 0; $i < $num_files; $i++ ) {
  966. $this->QueueCommand(MPD_CMD_PLADD,$trackArray[$i]);
  967. }
  968. $resp = $this->SendCommandQueue();
  969. $this->RefreshInfo();
  970. if ( $this->debugging ) echo "mpd->PLAddBulk() / return\n";
  971. return $resp;
  972. }
  973. /* PLAdd()
  974. *
  975. * Adds the file <file> to the end of the playlist. <file> must be a track in the MPD database.
  976. */
  977. function PLAdd($fileName) {
  978. if ( $this->debugging ) echo "mpd->PLAdd()\n";
  979. if ( ! is_null($resp = $this->SendCommand(MPD_CMD_PLADD,$fileName))) $this->RefreshInfo();
  980. if ( $this->debugging ) echo "mpd->PLAdd() / return\n";
  981. return $resp;
  982. }
  983. /* PLMoveTrack()
  984. *
  985. * Moves track number <origPos> to position <newPos> in the playlist. This is used to reorder
  986. * the songs in the playlist.
  987. */
  988. function PLMoveTrack($origPos, $newPos) {
  989. if ( $this->debugging ) echo "mpd->PLMoveTrack()\n";
  990. if ( ! is_numeric($origPos) ) {
  991. $this->errStr = "PLMoveTrack(): argument 1 must be numeric";
  992. return NULL;
  993. }
  994. if ( $origPos < 0 or $origPos > $this->playlist_count ) {
  995. $this->errStr = "PLMoveTrack(): argument 1 out of range";
  996. return NULL;
  997. }
  998. if ( $newPos < 0 ) $newPos = 0;
  999. if ( $newPos > $this->playlist_count ) $newPos = $this->playlist_count;
  1000. if ( ! is_null($resp = $this->SendCommand(MPD_CMD_PLMOVETRACK,$origPos,$newPos))) $this->RefreshInfo();
  1001. if ( $this->debugging ) echo "mpd->PLMoveTrack() / return\n";
  1002. return $resp;
  1003. }
  1004. /* PLShuffle()
  1005. *
  1006. * Randomly reorders the songs in the playlist.
  1007. */
  1008. function PLShuffle() {
  1009. if ( $this->debugging ) echo "mpd->PLShuffle()\n";
  1010. if ( ! is_null($resp = $this->SendCommand(MPD_CMD_PLSHUFFLE))) $this->RefreshInfo();
  1011. if ( $this->debugging ) echo "mpd->PLShuffle() / return\n";
  1012. return $resp;
  1013. }
  1014. /* PLLoad()
  1015. *
  1016. * Retrieves the playlist from <file>.m3u and loads it into the current playlist.
  1017. */
  1018. function PLLoad($file) {
  1019. if ( $this->debugging ) echo "mpd->PLLoad()\n";
  1020. if ( ! is_null($resp = $this->SendCommand(MPD_CMD_PLLOAD,$file))) $this->RefreshInfo();
  1021. if ( $this->debugging ) echo "mpd->PLLoad() / return\n";
  1022. return $resp;
  1023. }
  1024. /* PLSave()
  1025. *
  1026. * Saves the playlist to <file>.m3u for later retrieval. The file is saved in the MPD playlist
  1027. * directory.
  1028. */
  1029. function PLSave($file) {
  1030. if ( $this->debugging ) echo "mpd->PLSave()\n";
  1031. $resp = $this->SendCommand(MPD_CMD_PLSAVE,$file);
  1032. if ( $this->debugging ) echo "mpd->PLSave() / return\n";
  1033. return $resp;
  1034. }
  1035. /* PLClear()
  1036. *
  1037. * Empties the playlist.
  1038. */
  1039. function PLClear() {
  1040. if ( $this->debugging ) echo "mpd->PLClear()\n";
  1041. if ( ! is_null($resp = $this->SendCommand(MPD_CMD_PLCLEAR))) $this->RefreshInfo();
  1042. if ( $this->debugging ) echo "mpd->PLClear() / return\n";
  1043. return $resp;
  1044. }
  1045. /* PLRemove()
  1046. *
  1047. * Removes track <id> from the playlist.
  1048. */
  1049. function PLRemove($id) {
  1050. if ( $this->debugging ) echo "mpd->PLRemove()\n";
  1051. if ( ! is_numeric($id) ) {
  1052. $this->errStr = "PLRemove() : argument 1 must be a numeric value";
  1053. return NULL;
  1054. }
  1055. if ( ! is_null($resp = $this->SendCommand(MPD_CMD_PLREMOVE,$id))) $this->RefreshInfo();
  1056. if ( $this->debugging ) echo "mpd->PLRemove() / return\n";
  1057. return $resp;
  1058. }
  1059. /* SetRepeat()
  1060. *
  1061. * Enables 'loop' mode -- tells MPD continually loop the playlist. The <repVal> parameter
  1062. * is either 1 (on) or 0 (off).
  1063. */
  1064. function SetRepeat($repVal) {
  1065. if ( $this->debugging ) echo "mpd->SetRepeat()\n";
  1066. $rpt = $this->SendCommand(MPD_CMD_REPEAT,$repVal);
  1067. $this->repeat = $repVal;
  1068. if ( $this->debugging ) echo "mpd->SetRepeat() / return\n";
  1069. return $rpt;
  1070. }
  1071. /* SetRandom()
  1072. *
  1073. * Enables 'randomize' mode -- tells MPD to play songs in the playlist in random order. The
  1074. * <rndVal> parameter is either 1 (on) or 0 (off).
  1075. */
  1076. function SetRandom($rndVal) {
  1077. if ( $this->debugging ) echo "mpd->SetRandom()\n";
  1078. $resp = $this->SendCommand(MPD_CMD_RANDOM,$rndVal);
  1079. $this->random = $rndVal;
  1080. if ( $this->debugging ) echo "mpd->SetRandom() / return\n";
  1081. return $resp;
  1082. }
  1083. /* Shutdown()
  1084. *
  1085. * Shuts down the MPD server (aka sends the KILL command). This closes the current connection,
  1086. * and prevents future communication with the server.
  1087. */
  1088. function Shutdown() {
  1089. if ( $this->debugging ) echo "mpd->Shutdown()\n";
  1090. $resp = $this->SendCommand(MPD_CMD_SHUTDOWN);
  1091. $this->connected = FALSE;
  1092. unset($this->mpd_version);
  1093. unset($this->errStr);
  1094. unset($this->mpd_sock);
  1095. if ( $this->debugging ) echo "mpd->Shutdown() / return\n";
  1096. return $resp;
  1097. }
  1098. /* DBRefresh()
  1099. *
  1100. * Tells MPD to rescan the music directory for new tracks, and to refresh the Database. Tracks
  1101. * cannot be played unless they are in the MPD database.
  1102. */
  1103. function DBRefresh() {
  1104. if ( $this->debugging ) echo "mpd->DBRefresh()\n";
  1105. $resp = $this->SendCommand(MPD_CMD_REFRESH);
  1106. // Update local variables
  1107. $this->RefreshInfo();
  1108. if ( $this->debugging ) echo "mpd->DBRefresh() / return\n";
  1109. return $resp;
  1110. }
  1111. /* Play()
  1112. *
  1113. * Begins playing the songs in the MPD playlist.
  1114. */
  1115. function Play() {
  1116. if ( $this->debugging ) echo "mpd->Play()\n";
  1117. if ( ! is_null($rpt = $this->SendCommand(MPD_CMD_PLAY) )) $this->RefreshInfo();
  1118. if ( $this->debugging ) echo "mpd->Play() / return\n";
  1119. return $rpt;
  1120. }
  1121. /* Stop()
  1122. *
  1123. * Stops playing the MPD.
  1124. */
  1125. function Stop() {
  1126. if ( $this->debugging ) echo "mpd->Stop()\n";
  1127. if ( ! is_null($rpt = $this->SendCommand(MPD_CMD_STOP) )) $this->RefreshInfo();
  1128. if ( $this->debugging ) echo "mpd->Stop() / return\n";
  1129. return $rpt;
  1130. }
  1131. /* Pause()
  1132. *
  1133. * Toggles pausing on the MPD. Calling it once will pause the player, calling it again
  1134. * will unpause.
  1135. */
  1136. function Pause() {
  1137. if ( $this->debugging ) echo "mpd->Pause()\n";
  1138. if ( ! is_null($rpt = $this->SendCommand(MPD_CMD_PAUSE) )) $this->RefreshInfo();
  1139. if ( $this->debugging ) echo "mpd->Pause() / return\n";
  1140. return $rpt;
  1141. }
  1142. /* SeekTo()
  1143. *
  1144. * Skips directly to the <idx> song in the MPD playlist.
  1145. */
  1146. function SkipTo($idx) {
  1147. if ( $this->debugging ) echo "mpd->SkipTo()\n";
  1148. if ( ! is_numeric($idx) ) {
  1149. $this->errStr = "SkipTo() : argument 1 must be a numeric value";
  1150. return NULL;
  1151. }
  1152. if ( ! is_null($rpt = $this->SendCommand(MPD_CMD_PLAY,$idx))) $this->RefreshInfo();
  1153. if ( $this->debugging ) echo "mpd->SkipTo() / return\n";
  1154. return $idx;
  1155. }
  1156. /* SeekTo()
  1157. *
  1158. * Skips directly to a given position within a track in the MPD playlist. The <pos> argument,
  1159. * given in seconds, is the track position to locate. The <track> argument, if supplied is
  1160. * the track number in the playlist. If <track> is not specified, the current track is assumed.
  1161. */
  1162. function SeekTo($pos, $track = -1) {
  1163. if ( $this->debugging ) echo "mpd->SeekTo()\n";
  1164. if ( ! is_numeric($pos) ) {
  1165. $this->errStr = "SeekTo() : argument 1 must be a numeric value";
  1166. return NULL;
  1167. }
  1168. if ( ! is_numeric($track) ) {
  1169. $this->errStr = "SeekTo() : argument 2 must be a numeric value";
  1170. return NULL;
  1171. }
  1172. if ( $track == -1 ) {
  1173. $track = $this->current_track_id;
  1174. }
  1175. if ( ! is_null($rpt = $this->SendCommand(MPD_CMD_SEEK,$track,$pos))) $this->RefreshInfo();
  1176. if ( $this->debugging ) echo "mpd->SeekTo() / return\n";
  1177. return $pos;
  1178. }
  1179. /* Next()
  1180. *
  1181. * Skips to the next song in the MPD playlist. If not playing, returns an error.
  1182. */
  1183. function Next() {
  1184. if ( $this->debugging ) echo "mpd->Next()\n";
  1185. if ( ! is_null($rpt = $this->SendCommand(MPD_CMD_NEXT))) $this->RefreshInfo();
  1186. if ( $this->debugging ) echo "mpd->Next() / return\n";
  1187. return $rpt;
  1188. }
  1189. /* Previous()
  1190. *
  1191. * Skips to the previous song in the MPD playlist. If not playing, returns an error.
  1192. */
  1193. function Previous() {
  1194. if ( $this->debugging ) echo "mpd->Previous()\n";
  1195. if ( ! is_null($rpt = $this->SendCommand(MPD_CMD_PREV))) $this->RefreshInfo();
  1196. if ( $this->debugging ) echo "mpd->Previous() / return\n";
  1197. return $rpt;
  1198. }
  1199. /* Search()
  1200. *
  1201. * Searches the MPD database. The search <type> should be one of the following:
  1202. * MPD_SEARCH_ARTIST, MPD_SEARCH_TITLE, MPD_SEARCH_ALBUM
  1203. * The search <string> is a case-insensitive locator string. Anything that contains
  1204. * <string> will be returned in the results.
  1205. */
  1206. function Search($type,$string) {
  1207. if ( $this->debugging ) echo "mpd->Search()\n";
  1208. if ( $type != MPD_SEARCH_ARTIST and
  1209. $type != MPD_SEARCH_ALBUM and
  1210. $type != MPD_SEARCH_TITLE ) {
  1211. $this->errStr = "mpd->Search(): invalid search type";
  1212. return NULL;
  1213. } else {
  1214. if ( is_null($resp = $this->SendCommand(MPD_CMD_SEARCH,$type,$string))) return NULL;
  1215. $searchlist = $this->_parseFileListResponse($resp);
  1216. }
  1217. if ( $this->debugging ) echo "mpd->Search() / return ".print_r($searchlist)."\n";
  1218. return $searchlist;
  1219. }
  1220. /* Find()
  1221. *
  1222. * Find() looks for exact matches in the MPD database. The find <type> should be one of
  1223. * the following:
  1224. * MPD_SEARCH_ARTIST, MPD_SEARCH_TITLE, MPD_SEARCH_ALBUM
  1225. * The find <string> is a case-insensitive locator string. Anything that exactly matches
  1226. * <string> will be returned in the results.
  1227. */
  1228. function Find($type,$string) {
  1229. if ( $this->debugging ) echo "mpd->Find()\n";
  1230. if ( $type != MPD_SEARCH_ARTIST and
  1231. $type != MPD_SEARCH_ALBUM and
  1232. $type != MPD_SEARCH_TITLE ) {
  1233. $this->errStr = "mpd->Find(): invalid find type";
  1234. return NULL;
  1235. } else {
  1236. if ( is_null($resp = $this->SendCommand(MPD_CMD_FIND,$type,$string))) return NULL;
  1237. $searchlist = $this->_parseFileListResponse($resp);
  1238. }
  1239. if ( $this->debugging ) echo "mpd->Find() / return ".print_r($searchlist)."\n";
  1240. return $searchlist;
  1241. }
  1242. /* Disconnect()
  1243. *
  1244. * Closes the connection to the MPD server.
  1245. */
  1246. function Disconnect() {
  1247. if ( $this->debugging ) echo "mpd->Disconnect()\n";
  1248. fclose($this->mpd_sock);
  1249. $this->connected = FALSE;
  1250. unset($this->mpd_version);
  1251. unset($this->errStr);
  1252. unset($this->mpd_sock);
  1253. }
  1254. /* GetArtists()
  1255. *
  1256. * Returns the list of artists in the database in an associative array.
  1257. */
  1258. function GetArtists() {
  1259. if ( $this->debugging ) echo "mpd->GetArtists()\n";
  1260. if ( is_null($resp = $this->SendCommand(MPD_CMD_TABLE, MPD_TBL_ARTIST))) return NULL;
  1261. $arArray = array();
  1262. $arLine = strtok($resp,"\n");
  1263. $arName = "";
  1264. $arCounter = -1;
  1265. while ( $arLine ) {
  1266. list ( $element, $value ) = split(": ",$arLine);
  1267. if ( $element == "Artist" ) {
  1268. $arCounter++;
  1269. $arName = $value;
  1270. $arArray[$arCounter] = $arName;
  1271. }
  1272. $arLine = strtok("\n");
  1273. }
  1274. if ( $this->debugging ) echo "mpd->GetArtists()\n";
  1275. return $arArray;
  1276. }
  1277. /* GetAlbums()
  1278. *
  1279. * Returns the list of albums in the database in an associative array. Optional parameter
  1280. * is an artist Name which will list all albums by a particular artist.
  1281. */
  1282. function GetAlbums( $ar = NULL) {
  1283. if ( $this->debugging ) echo "mpd->GetAlbums()\n";
  1284. if ( is_null($resp = $this->SendCommand(MPD_CMD_TABLE, MPD_TBL_ALBUM, $ar ))) return NULL;
  1285. $alArray = array();
  1286. $alLine = strtok($resp,"\n");
  1287. $alName = "";
  1288. $alCounter = -1;
  1289. while ( $alLine ) {
  1290. list ( $element, $value ) = split(": ",$alLine);
  1291. if ( $element == "Album" ) {
  1292. $alCounter++;
  1293. $alName = $value;
  1294. $alArray[$alCounter] = $alName;
  1295. }
  1296. $alLine = strtok("\n");
  1297. }
  1298. if ( $this->debugging ) echo "mpd->GetAlbums()\n";
  1299. return $alArray;
  1300. }
  1301. //*******************************************************************************//
  1302. //***************************** INTERNAL FUNCTIONS ******************************//
  1303. //*******************************************************************************//
  1304. /* _computeVersionValue()
  1305. *
  1306. * Computes a compatibility value from a version string
  1307. *
  1308. */
  1309. function _computeVersionValue($verStr) {
  1310. list ($ver_maj, $ver_min, $ver_rel ) = split("\.",$verStr);
  1311. return ( 100 * $ver_maj ) + ( 10 * $ver_min ) + ( $ver_rel );
  1312. }
  1313. /* _checkCompatibility()
  1314. *
  1315. * Check MPD command compatibility against our internal table. If there is no version
  1316. * listed in the table, allow it by default.
  1317. */
  1318. function _checkCompatibility($cmd) {
  1319. // Check minimum compatibility
  1320. if (isset($this->COMPATIBILITY_MIN_TBL[$cmd])) {
  1321. $req_ver_low = $this->COMPATIBILITY_MIN_TBL[$cmd];
  1322. $req_ver_hi = $this->COMPATIBILITY_MAX_TBL[$cmd];
  1323. } else {
  1324. $req_ver_low = false;
  1325. $req_ver_hi = false;
  1326. }
  1327. $mpd_ver = $this->_computeVersionValue($this->mpd_version);
  1328. if ( $req_ver_low ) {
  1329. $req_ver = $this->_computeVersionValue($req_ver_low);
  1330. if ( $mpd_ver < $req_ver ) {
  1331. $this->errStr = "Command '$cmd' is not compatible with this version of MPD, version ".$req_ver_low." required";
  1332. return FALSE;
  1333. }
  1334. }
  1335. // Check maxmum compatibility -- this will check for deprecations
  1336. if ( $req_ver_hi ) {
  1337. $req_ver = $this->_computeVersionValue($req_ver_hi);
  1338. if ( $mpd_ver > $req_ver ) {
  1339. $this->errStr = "Command '$cmd' has been deprecated in this version of MPD.";
  1340. return FALSE;
  1341. }
  1342. }
  1343. return TRUE;
  1344. }
  1345. /* _parseFileListResponse()
  1346. *
  1347. * Builds a multidimensional array with MPD response lists.
  1348. *
  1349. * NOTE: This function is used internally within the class. It should not be used.
  1350. */
  1351. function _parseFileListResponse($resp) {
  1352. if ( is_null($resp) ) {
  1353. return NULL;
  1354. } else {
  1355. $plistArray = array();
  1356. $plistLine = strtok($resp,"\n");
  1357. $plistFile = "";
  1358. $plCounter = -1;
  1359. while ( $plistLine ) {
  1360. list ( $element, $value ) = split(": ",$plistLine);
  1361. if ( $element == "file" ) {
  1362. $plCounter++;
  1363. $plistFile = $value;
  1364. $plistArray[$plCounter]["file"] = $plistFile;
  1365. } else {
  1366. $plistArray[$plCounter][$element] = $value;
  1367. }
  1368. $plistLine = strtok("\n");
  1369. }
  1370. }
  1371. return $plistArray;
  1372. }
  1373. /* RefreshInfo()
  1374. *
  1375. * Updates all class properties with the values from the MPD server.
  1376. *
  1377. * NOTE: This function is automatically called upon Connect() as of v1.1.
  1378. */
  1379. function RefreshInfo() {
  1380. // Get the Server Statistics
  1381. $statStr = $this->SendCommand(MPD_CMD_STATISTICS);
  1382. if ( !$statStr ) {
  1383. return NULL;
  1384. } else {
  1385. $stats = array();
  1386. $statLine = strtok($statStr,"\n");
  1387. while ( $statLine ) {
  1388. list ( $element, $value ) = split(": ",$statLine);
  1389. $stats[$element] = $value;
  1390. $statLine = strtok("\n");
  1391. }
  1392. }
  1393. // Get the Server Status
  1394. $statusStr = $this->SendCommand(MPD_CMD_STATUS);
  1395. if ( ! $statusStr ) {
  1396. return NULL;
  1397. } else {
  1398. $status = array();
  1399. $statusLine = strtok($statusStr,"\n");
  1400. while ( $statusLine ) {
  1401. list ( $element, $value ) = split(": ",$statusLine);
  1402. $status[$element] = $value;
  1403. $statusLine = strtok("\n");
  1404. }
  1405. }
  1406. // Get the Playlist
  1407. $plStr = $this->SendCommand(MPD_CMD_PLLIST);
  1408. $this->playlist = $this->_parseFileListResponse($plStr);
  1409. $this->playlist_count = count($this->playlist);
  1410. // Set Misc Other Variables
  1411. $this->state = $status['state'];
  1412. if ( ($this->state == MPD_STATE_PLAYING) || ($this->state == MPD_STATE_PAUSED) ) {
  1413. $this->current_track_id = $status['song'];
  1414. list ($this->current_track_position, $this->current_track_length ) = split(":",$status['time']);
  1415. } else {
  1416. $this->current_track_id = -1;
  1417. $this->current_track_position = -1;
  1418. $this->current_track_length = -1;
  1419. }
  1420. $this->repeat = $status['repeat'];
  1421. $this->random = $status['random'];
  1422. $this->db_last_refreshed = $stats['db_update'];
  1423. $this->volume = $status['volume'];
  1424. $this->uptime = $stats['uptime'];
  1425. $this->playtime = $stats['playtime'];
  1426. if (isset($stats['songs_played'])) {
  1427. $this->num_songs_played = $stats['songs_played'];
  1428. } else {
  1429. $this->num_songs_played = false;
  1430. }
  1431. if (isset($stats['num_artists'])) {
  1432. $this->num_artists = $stats['num_artists'];
  1433. } else {
  1434. $this->num_artists = false;
  1435. }
  1436. if (isset($stats['num_songs'])) {
  1437. $this->num_songs = $stats['num_songs'];
  1438. } else {
  1439. $this->num_songs = false;
  1440. }
  1441. if (isset($stats['num_albums'])) {
  1442. $this->num_albums = $stats['num_albums'];
  1443. } else {
  1444. $this->num_albums = false;
  1445. }
  1446. return TRUE;
  1447. }
  1448. /* ------------------ DEPRECATED METHODS -------------------*/
  1449. /* GetStatistics()
  1450. *
  1451. * Retrieves the 'statistics' variables from the server and tosses them into an array.
  1452. *
  1453. * NOTE: This function really should not be used. Instead, use $this->[variable]. The function
  1454. * will most likely be deprecated in future releases.
  1455. */
  1456. function GetStatistics() {
  1457. if ( $this->debugging ) echo "mpd->GetStatistics()\n";
  1458. $stats = $this->SendCommand(MPD_CMD_STATISTICS);
  1459. if ( !$stats ) {
  1460. return NULL;
  1461. } else {
  1462. $statsArray = array();
  1463. $statsLine = strtok($stats,"\n");
  1464. while ( $statsLine ) {
  1465. list ( $element, $value ) = split(": ",$statsLine);
  1466. $statsArray[$element] = $value;
  1467. $statsLine = strtok("\n");
  1468. }
  1469. }
  1470. if ( $this->debugging ) echo "mpd->GetStatistics() / return: " . print_r($statsArray) ."\n";
  1471. return $statsArray;
  1472. }
  1473. /* GetStatus()
  1474. *
  1475. * Retrieves the 'status' variables from the server and tosses them into an array.
  1476. *
  1477. * NOTE: This function really should not be used. Instead, use $this->[variable]. The function
  1478. * will most likely be deprecated in future releases.
  1479. */
  1480. function GetStatus() {
  1481. if ( $this->debugging ) echo "mpd->GetStatus()\n";
  1482. $status = $this->SendCommand(MPD_CMD_STATUS);
  1483. if ( ! $status ) {
  1484. return NULL;
  1485. } else {
  1486. $statusArray = array();
  1487. $statusLine = strtok($status,"\n");
  1488. while ( $statusLine ) {
  1489. list ( $element, $value ) = split(": ",$statusLine);
  1490. $statusArray[$element] = $value;
  1491. $statusLine = strtok("\n");
  1492. }
  1493. }
  1494. if ( $this->debugging ) echo "mpd->GetStatus() / return: " . print_r($statusArray) ."\n";
  1495. return $statusArray;
  1496. }
  1497. /* GetVolume()
  1498. *
  1499. * Retrieves the mixer volume from the server.
  1500. *
  1501. * NOTE: This function really should not be used. Instead, use $this->volume. The function
  1502. * will most likely be deprecated in future releases.
  1503. */
  1504. function GetVolume() {
  1505. if ( $this->debugging ) echo "mpd->GetVolume()\n";
  1506. $volLine = $this->SendCommand(MPD_CMD_STATUS);
  1507. if ( ! $volLine ) {
  1508. return NULL;
  1509. } else {
  1510. list ($vol) = sscanf($volLine,"volume: %d");
  1511. }
  1512. if ( $this->debugging ) echo "mpd->GetVolume() / return: $vol\n";
  1513. return $vol;
  1514. }
  1515. /* GetPlaylist()
  1516. *
  1517. * Retrieves the playlist from the server and tosses it into a multidimensional array.
  1518. *
  1519. * NOTE: This function really should not be used. Instead, use $this->playlist. The function
  1520. * will most likely be deprecated in future releases.
  1521. */
  1522. function GetPlaylist() {
  1523. if ( $this->debugging ) echo "mpd->GetPlaylist()\n";
  1524. $resp = $this->SendCommand(MPD_CMD_PLLIST);
  1525. $playlist = $this->_parseFileListResponse($resp);
  1526. if ( $this->debugging ) echo "mpd->GetPlaylist() / return ".print_r($playlist)."\n";
  1527. return $playlist;
  1528. }
  1529. /* ----------------- Command compatibility tables --------------------- */
  1530. var $COMPATIBILITY_MIN_TBL = array(
  1531. MPD_CMD_SEEK => "0.9.1" ,
  1532. MPD_CMD_PLMOVE => "0.9.1" ,
  1533. MPD_CMD_RANDOM => "0.9.1" ,
  1534. MPD_CMD_PLSWAPTRACK => "0.9.1" ,
  1535. MPD_CMD_PLMOVETRACK => "0.9.1" ,
  1536. MPD_CMD_PASSWORD => "0.10.0" ,
  1537. MPD_CMD_SETVOL => "0.10.0"
  1538. );
  1539. var $COMPATIBILITY_MAX_TBL = array(
  1540. MPD_CMD_VOLUME => "0.10.0"
  1541. );
  1542. } // ---------------------------- end of class ------------------------------
  1543. ?>