3     Pitchfork Music Player Daemon Client
 
   4     Copyright (C) 2007  Roger Bystrøm
 
   6     This program is free software; you can redistribute it and/or modify
 
   7     it under the terms of the GNU General Public License as published by
 
   8     the Free Software Foundation; version 2 of the License.
 
  10     This program is distributed in the hope that it will be useful,
 
  11     but WITHOUT ANY WARRANTY; without even the implied warranty of
 
  12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
  13     GNU General Public License for more details.
 
  15     You should have received a copy of the GNU General Public License along
 
  16     with this program; if not, write to the Free Software Foundation, Inc.,
 
  17     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
  21         require_once("../inc/base.php");
 
  22         require_once("../inc/JSON.php");
 
  24         define('PF_FAILURE', 'failed');
 
  26         $META_SEARCH = array("any", "Artist", "Title", "Album", "Genre", "filename", "Composer", "Performer", "Date" ); // "lyrics"...
 
  27         $PL_SEARCH = array("any", "Artist", "Title", "Album", "Genre", "filename", "Composer", "Performer", "Date" ); // "lyrics"...
 
  29         header("Content-Type: text/plain; charset=UTF-8");
 
  31         function ids_to_list($sel) {
 
  32                 $arr = explode(";", trim($sel));
 
  35                 foreach($arr as $val) {
 
  36                         $pos = strpos($val, "-");
 
  41                                 $tmp = explode("-", $val);
 
  42                                 $res = array_merge($res, range($tmp[0], $tmp[1])); 
 
  48         function selection_to_list($sel) {
 
  49                 $res = ids_to_list($sel);
 
  50                 sort($sel, SORT_NUMERIC);
 
  54         function selection_to_reverse_list($sel) {
 
  55                 $res = ids_to_list($sel);
 
  56                 rsort($res, SORT_NUMERIC);
 
  60         function search_add($db, $pl, $search) {
 
  61                 $tmp = $db->find($search, true);
 
  62                 foreach($tmp as &$a) {
 
  63                         $pl->addSong($a['file']);
 
  70         function parsePlaylist($txt, $type = false) {
 
  71                 $txt = explode("\n", $txt); // trim will remove the \r
 
  73                 if($type=="pls"||$type===false) {
 
  76                                 if(stripos($t, "file")!==false) { 
 
  77                                         $pos = spliti("^file[0-9]*=", $t);
 
  78                                         if(count($pos)==2&&strlen($pos[1])>0)
 
  83                 else if($type=="m3u" || ($type===false&&count($res)==0) ) {
 
  86                                 if(strpos($t, "#")!==false||strlen($t)==0) {
 
  87                                         echo "skipping: $t\n";
 
  95         function handle_playlist_url($pl, $url, $get = false) {
 
  97                         $fp = @fopen($url, "r");
 
  99                                 $type = substr($url, strlen($url)-3); // just get three last chars..
 
 100                                 $md = stream_get_meta_data($fp);
 
 101                                 $md = $md['wrapper_data'];
 
 103                                         if(stripos($m, "content-type:")==0) {
 
 104                                                 if(stripos($m, "audio/x-scpls")) {
 
 107                                                 else if(stripos($m, "audio/x-mpegurl")
 
 108                                                      || stripos($m, "audio/mpegurl")) {
 
 113                                 $type = strtolower($type);
 
 114                                 $data = stream_get_contents($fp);
 
 115                                 $stuff = parsePlaylist($data, $type);
 
 116                                 foreach($stuff as $s) {
 
 128                         $context = stream_context_create($opts);
 
 129                         $fp = @fopen($url, "r", false, $context);
 
 133                                 $md = array(); // head did not work....
 
 136                                 $md = stream_get_meta_data($fp);
 
 137                                 $md = $md['wrapper_data'];
 
 140                         $type = substr($url, strlen($url)-3); // just get three last chars..
 
 142                         /* these lists are probably incomplete, make a ticket if 
 
 143                            you want something added */
 
 145                                 if(stripos($m, "content-type:")==0) {
 
 146                                         if(stripos($m, "audio/x-scpls")||
 
 147                                            stripos($m, "audio/x-mpegurl")||
 
 148                                            stripos($m, "audio/mpegurl")) {
 
 149                                                 return handle_playlist_url($pl, $url, true);
 
 151                                         else if(stripos($m, "audio/mpeg")||
 
 152                                                 stripos($m, "audio/mpeg3")||
 
 153                                                 stripos($m, "audio/x-mpeg3")||
 
 154                                                 stripos($m, "audio/mpeg2")||
 
 155                                                 stripos($m, "audio/x-mpeg2")||
 
 156                                                 stripos($m, "application/ogg")||
 
 157                                                 stripos($m, "audio/x-ogg")||
 
 158                                                 stripos($m, "audio/mp4")||
 
 159                                                 stripos($m, "audio/x-mod")||
 
 160                                                 stripos($m, "audio/mod")||
 
 161                                                 stripos($m, "audio/basic")||
 
 162                                                 stripos($m, "audio/x-basic")||
 
 163                                                 stripos($m, "audio/wav")||
 
 164                                                 stripos($m, "audio/x-wav")||
 
 165                                                 stripos($m, "audio/flac")||
 
 166                                                 stripos($m, "audio/x-flac")
 
 173                         $type = strtolower($type);
 
 174                         $type4 = strtolower($url, strlen($url)-4);
 
 175                         if($type=="m3u"||$type=="pls") {
 
 176                                 return handle_playlist_url($pl, $url, true);
 
 178                         else if($type=="ogg"||$type=="mp3"||$type=="mp2"||$type=="wav"
 
 179                                 ||$type==".au"||$type=="m4a"||$type4=="flac"||$type4=="aiff") {
 
 180                                 // ugh, just try to add it... 
 
 188         function array_to_json(&$arr) {
 
 190                 if(function_exists("json_encode")) {
 
 191                         echo json_encode($arr);
 
 193                         $json = new Services_JSON();
 
 199         /* for auto-play-start on empty */
 
 200         $playlist_empty = false;
 
 201         $something_added = false;
 
 204         $pl = get_playlist();
 
 206                 $v = array("connection" => PF_FAILURE);
 
 207                 echo array_to_json($v);
 
 210         else if(isset($_GET['add'])||isset($_GET['ma'])||isset($_GET['searchadd'])) {
 
 211                 /* for automatic playback start */
 
 213                         $s = $pl->getStatus();
 
 214                         if(isset($s['playlistlength'])&&intval($s['playlistlength'])==0) {
 
 215                                 $playlist_empty = true;
 
 218                 catch (PEAR_Exception $e) {
 
 219                         $v = array("connection" => PF_FAILURE);
 
 220                         echo array_to_json($v);
 
 225         if(isset($_GET['playlist'])) {
 
 226                 $act = $_GET['playlist'];
 
 228                         if($act=="move"&&isset($_GET['from'])&&isset($_GET['to'])) {
 
 229                                 // todo: sanity check
 
 231                                 if($pl->moveSongId($_GET['from'], $_GET['to'])) 
 
 232                                         $response = array('result' => "ok");
 
 233                                 else $response = array ('result' => PF_FAILURE);
 
 236                         else if($act=="info"&&isset($_POST['ids'])) {
 
 237                                 $list = ids_to_list($_POST['ids']);
 
 239                                 foreach($list as $id) {
 
 240                                         $tmp = $pl->getPlaylistInfoId($id);
 
 241                                         if(isset($tmp['file']))
 
 242                                                 $ret[] = $tmp['file'][0];
 
 246                                 $json['result'] = &$ret;
 
 249                                 $json = array("result" => PF_FAILURE);
 
 252                 catch(PEAR_Exception $e) {
 
 253                         $json = array ('result' => PF_FAILURE);
 
 256         else if(isset($_GET['rangemove'])&&is_numeric(trim($_GET['rangemove']))&&isset($_GET['elems'])) {
 
 258                 $dest = intval($_GET['rangemove']);
 
 261                         $list = selection_to_reverse_list($_GET['elems']);
 
 262                         foreach($list as &$pos) {
 
 263                                 //echo $pos-$pos_offset . "=>" .$dest."\n";
 
 265                                 /* this means we've moved above the place where changing the position will
 
 266                                  * have any effect on the list */
 
 267                                 if($dest>$pos&&$pos_offset!=0) {
 
 272                                 $pl->moveSong($pos-$pos_offset, $dest);
 
 273                                 if($dest>$pos-$pos_offset) {
 
 274                                         /* means we yanked something from over destination */
 
 277                                 else if($dest<$pos-$pos_offset) {
 
 278                                         /* means we yanked something from below destination */
 
 282                                         /* moved it to our selves O_o */
 
 288                 catch(PEAR_Exception $e) {
 
 290                 $json = array ('result' => $res);
 
 292         else if(isset($_GET['ping'])) {
 
 294                 $json = array("result" => $result);
 
 296         else if(isset($_GET['volume'])&&is_numeric(trim($_GET['volume']))) {
 
 299                         $volume = trim($_GET['volume']);
 
 300                         $play = get_playback();
 
 301                         if($play->setVolume($volume)) 
 
 305                 catch(PEAR_Exception $e) {
 
 307                 $json = array("result" => $res);
 
 309         else if(isset($_GET['position'])&&is_numeric(trim($_GET['position']))
 
 310                 && isset($_GET['id']) && is_numeric(trim($_GET['id']))) {
 
 311                 $result = PF_FAILURE;
 
 313                         $pos = trim($_GET['position']);
 
 314                         $id = trim($_GET['id']);
 
 315                         $play = get_playback();
 
 316                         if($play->seekId($id, $pos)) 
 
 320                 catch(PEAR_Exception $e) {
 
 322                 $json = array("result" => $result);
 
 325         else if(isset($_GET['currentsong'])) {
 
 328                         $res = $pl->getCurrentSong();
 
 332                 catch(PEAR_Exception $e) {
 
 334                 $json = array("result" => $res);
 
 336         else if(isset($_GET['dirlist'])) {
 
 337                 $dir = trim($_GET['dirlist']);
 
 343                 if(isset($_GET['type'])&&is_numeric($_GET['type'])) {
 
 344                         $type = $_GET['type'];
 
 346                 if(is_null($dir)||$dir=="")
 
 350                         $db = get_database();
 
 352                         if($type==$ALBUM||$type==$ARTIST) {
 
 354                                 if(($t = strrpos($dir, "/")) !== false && $t == strlen($dir)-1) {
 
 355                                         $dir = substr($dir, $t+1);
 
 357                                 if(strlen($dir)==0) {
 
 358                                         $type = $type==$ALBUM?"Album":"Artist";
 
 359                                         $res = array(strtolower($type) => $db->getMetadata($type));
 
 364                                                 $res["artist"] = $db->getMetadata("Artist", "Album", $dir);
 
 365                                                 $res['filelist'] = $db->find(array("Album" => $dir), true);
 
 367                                         else if($type==$ARTIST) {
 
 368                                                 $res["album"] = $db->getMetadata("Album","Artist", $dir);
 
 369                                                 $res['filelist'] = $db->find(array("Artist" => $dir), true);
 
 374                                 $tmp = $db->getInfo($dir);
 
 377                                 if(isset($tmp['directory'])) {
 
 378                                         $res['directory'] =$tmp['directory'];
 
 380                                 if(isset($tmp['file'])) {
 
 381                                         $res['file'] = array();
 
 382                                         foreach($tmp['file'] as &$row) {
 
 383                                                 $res['file'][] = $row['file'];
 
 386                                 if(isset($tmp['playlist'])) {
 
 387                                         $res['playlist'] = $tmp['playlist'];
 
 394                 catch(PEAR_Exception $e) {
 
 396                 $json = array("result" => $res);
 
 398         else if(isset($_GET['act'])) {
 
 399                 $act = trim($_GET['act']);
 
 402                         $play = get_playback(); 
 
 405                                 if(isset($_GET['id'])&&is_numeric(trim($_GET['id']))) {
 
 406                                         if($play->playId(trim($_GET['id'])))
 
 409                                 else if(isset($_GET['pos'])&&is_numeric(trim($_GET['pos']))) {
 
 410                                         if($play->play(trim($_GET['pos'])))
 
 413                                 else if($play->play()) {
 
 417                         else if($act == "toggle") {
 
 421                         else if($act == "next") {
 
 422                                 if($play->nextSong())
 
 425                         else if( $act == "previous") {
 
 426                                 if($play->previousSong())
 
 429                         else if($act=="stop") {
 
 433                         else $result = "invalid command";
 
 436                 catch(PEAR_Exception $e) {
 
 439                 $json = array("result" => $result);
 
 441         else if(isset($_GET['add'])) {
 
 445                         if($pl->addSong($add)) {
 
 447                                 $something_added = true;
 
 450                 catch(PEAR_Exception $e) {
 
 452                 $json = array("result" => $res);
 
 454         else if(isset($_GET['remove'])) {
 
 455                 $arr = selection_to_reverse_list($_GET['remove']);
 
 458                         foreach($arr as &$val) {
 
 459                                 if(!$pl->deleteSong($val))
 
 463                 catch(PEAR_Exception $e) {
 
 466                 $json = array("result" => $res);
 
 468         else if(isset($_GET['updatedb'])) {
 
 472                         if($adm->updateDatabase())
 
 476                 catch(PEAR_Exception $e) {
 
 479                 $json = array("result" => $res);
 
 481         else if(isset($_GET['outputs'])||isset($_GET['output_e'])||isset($_GET['output_d'])) {
 
 484                         $admin = get_admin();
 
 485                         if(isset($_GET['outputs']))
 
 486                                 $res = $admin->getOutputs();
 
 487                         else if(isset($_GET['output_e'])&&is_numeric($_GET['output_e']))
 
 488                                 $res = $admin->enableOutput(trim($_GET['output_e']))?"1":PF_FAILURE;
 
 489                         else if(isset($_GET['output_d'])&&is_numeric($_GET['output_d']))
 
 490                                 $res = $admin->disableOutput(trim($_GET['output_d']))?"0":PF_FAILURE;
 
 491                         $admin->disconnect();
 
 493                 catch(PEAR_Exception $e) {
 
 496                 $json = array("result" => $res);
 
 498         else if(isset($_GET['random'])) {
 
 501                         $play = get_playback(); 
 
 502                         $val = $_GET['random']=="1";
 
 503                         if($play->random($val)) {
 
 508                 catch(PEAR_Exception $e) {
 
 510                 $json = array("result" => $res);
 
 513         else if(isset($_GET['repeat'])) {
 
 516                         $play = get_playback(); 
 
 517                         $val = $_GET['repeat']=="1";
 
 518                         if($play->repeat($val)) {
 
 523                 catch(PEAR_Exception $e) {
 
 525                 $json = array("result" => $res);
 
 527         else if(isset($_GET['xfade'])&&is_numeric($_GET['xfade'])) {
 
 530                         $play = get_playback(); 
 
 531                         if($play->setCrossfade(trim($_GET['xfade'])))
 
 536                 catch(PEAR_Exception $e) {
 
 538                 $json = array("result" => $res);
 
 540         else if(isset($_GET['quick_search'])) {
 
 541                 $dir = trim($_GET['quick_search']);
 
 544                         $search_dir = strrpos($dir, "/");
 
 546                                 $search_dir = substr($dir, 0, $search_dir);
 
 551                         $db = get_database(); 
 
 552                         $tmp = $db->getInfo($search_dir);
 
 553                         if(isset($tmp['directory'])) {
 
 556                                 foreach($tmp['directory'] as $key => &$value) {
 
 557                                         if(stripos($value, $dir)===0) {
 
 559                                                 $res[$key] = &$value;
 
 561                                         if($i>=20) /* return up to x entries */
 
 568                 catch(PEAR_Exception $e) {
 
 570                 $json = array("result" => $res);
 
 572         else if(isset($_GET['searchadd'])||isset($_GET['searchfile'])) {
 
 576                 if(isset($_GET['artist'])&&strlen($_GET['artist'])>0)
 
 577                         $artist = $_GET['artist'];
 
 578                 if(isset($_GET['album'])&&strlen($_GET['album'])>0)
 
 579                         $album = $_GET['album'];
 
 580                 if(!(is_null($artist)&&is_null($album))) {
 
 582                         $db = get_database();
 
 584                         if(!is_null($artist)) 
 
 585                                 $params["Artist"] = $artist;
 
 587                                 $params["Album"] = $album;
 
 590                         if(isset($_GET['searchadd'])) {
 
 591                                 if(search_add($db, $pl, $params)) {
 
 593                                         $something_added = true;
 
 595                                 else $res = "notfound";
 
 599                                 $res['filelist'] = $db->find($params, true);
 
 603                 catch(PEAR_Exception $e) {
 
 607                 $json = array("result" => $res);
 
 609         else if(((isset($_GET['metasearch'])&&is_numeric($_GET['metasearch']))||
 
 610                  (isset($_GET['plsearch'])&&is_numeric($_GET['plsearch'])))
 
 611                 &&isset($_GET['s'])) {
 
 612                 $plsearch = isset($_GET['plsearch']);
 
 614                 $type = intval($plsearch?$_GET['plsearch']:$_GET['metasearch']);
 
 615                 $search = $_GET['s'];
 
 617                 if($type>=0&&$type<count($plsearch?$PL_SEARCH:$META_SEARCH)) {
 
 620                                 if($plsearch&&$pl->hasFind()) {
 
 621                                         $tmp = $pl->find(array($PL_SEARCH[$type] => $search));
 
 624                                         $data = $pl->getPlaylistInfoId();
 
 625                                         if(isset($data['file']))
 
 626                                                 $data = $data['file'];
 
 628                                         $t = $PL_SEARCH[$type];
 
 629                                         foreach($data as &$song) {
 
 630                                                 if($type===0) { // any
 
 631                                                         foreach($song as &$e)
 
 632                                                                 if(stristr($e, $search)!==FALSE) {
 
 637                                                 else if(isset($song[$t]) && stristr($song[$t],$search)!==FALSE)
 
 642                                         $db = get_database();
 
 643                                         $tmp = $db->find(array($META_SEARCH[$type] => $search));
 
 648                                 $keys = array("Artist", "Title", "file", "Pos");
 
 649                                 foreach($tmp as &$row) {
 
 651                                         foreach($row as $key => &$val) {
 
 652                                                 if(in_array($key, $keys)!==FALSE)
 
 658                         catch(PEAR_Exception $e) {
 
 659                                 //$res = $e->getMessage();
 
 662                 else if($type==count($META_SEARCH)) { // search lyrics...
 
 663                         /* this should probably have been in metadata.php, but don't need 
 
 664                          * to change anything if we have it here, kiss */
 
 666                         if(is_dir($metadata_dir)&&is_readable($metadata_dir)) {
 
 667                                 $files = scandir($metadata_dir); 
 
 668                                 foreach($files as $file) {
 
 669                                         $pos = strrpos($file, ".lyric");
 
 670                                         if($pos!==false&&$pos==strlen($file)-6) {
 
 671                                                 $xml = @simplexml_load_file($metadata_dir . $file);
 
 672                                                 if($xml&&isset($xml->result[0])&&isset($xml->result[0]->lyric[0])) {
 
 673                                                         $l = (string)$xml->result[0]->lyric[0];
 
 674                                                         if(stripos($l, $search)!==false) {
 
 675                                                                 if(isset($xml->file)) {
 
 677                                                                         foreach($xml->file as $f) {
 
 679                                                                                 $e['file'] = (string)$f;
 
 680                                                                                 $e['Artist'] = (string)$xml->result[0]->artist[0];
 
 681                                                                                 $e['Title'] = (string)$xml->result[0]->title[0];
 
 686                                                                         $e['Artist'] = (string)$xml->result[0]->artist[0];
 
 687                                                                         $e['Title'] = (string)$xml->result[0]->title[0];
 
 695                         $db = get_database();
 
 698                         foreach($tmp as &$row) {
 
 699                                 $sr = $db->find(array("Artist" => $row['Artist'], "Title" => $row["Title"]));
 
 702                                 if(isset($sr[0])&&isset($sr[0]['file'])) {
 
 703                                         $row['file'] = $sr[0]['file'];
 
 709                 $json = array("result" => $res);
 
 711         else if(isset($_GET['ma'])) {
 
 712                 /* note to self: should merge single add with this */
 
 714                 if (!isset($HTTP_RAW_POST_DATA))
 
 715                    $HTTP_RAW_POST_DATA = file_get_contents("php://input");
 
 716                 $ma = explode("\n", $HTTP_RAW_POST_DATA);
 
 720                         $tmp = explode(":", $ma[0], 2);
 
 721                         if($tmp[0]=="baseartist") {
 
 722                                 $sparam['Artist'] = $tmp[1];
 
 724                         else if($tmp[0]=="basealbum") {
 
 725                                 $sparam['Album'] = $tmp[1];
 
 729                         foreach($ma as &$guom) {
 
 730                                 $v = explode(":", $guom, 2);
 
 734                                 if($v[0]=="file"||$v[0]=="directory") {
 
 736                                 } /* we should never get same as baseartist/album here, if we do I don't care */
 
 737                                 else if($v[0]=="album"||$v[0]=="artist") {
 
 739                                                 $sparam["Album"] = $v[1];
 
 740                                         else $sparam["Artist"] = $v[1];
 
 742                                                 $db = get_database();
 
 743                                         search_add($db, $pl, $sparam);
 
 745                                 else if($v[0]=="playlist") {
 
 746                                         $pl->loadPlaylist($v[1]);
 
 750                         $something_added = true;
 
 754                 catch(PEAR_Exception $e) { }
 
 755                 $json = array("result" => $res);
 
 757         else if(isset($_GET['playlist_rm'])||isset($_GET['playlist_load'])
 
 758                 ||isset($_GET['playlist_save'])||isset($_GET['playlist_add_url'])) {
 
 762                         if(isset($_GET['playlist_rm'])) {
 
 763                                 $res = $pl->deletePlaylist(trim($_GET['playlist_rm']));
 
 765                         else if(isset($_GET['playlist_load'])) {
 
 766                                 $res = $pl->loadPlaylist(trim($_GET['playlist_load']));
 
 768                         else if(isset($_GET['playlist_save'])) {
 
 769                                 $res = $pl->savePlaylist(trim($_GET['playlist_save']));
 
 771                         else if(isset($_GET['playlist_add_url'])) {
 
 772                                 $url = trim($_GET['playlist_add_url']);
 
 773                                 if(stripos($url, "http://")==0) {
 
 774                                         $res = handle_playlist_url($pl, $url);
 
 778                 catch(PEAR_Exception $e) {}
 
 779                 $res = $res?"ok":PF_FAILURE;
 
 780                 $json = array("result" => $res);
 
 782         else if(isset($_GET['clearerror'])) {
 
 784                 $json = array("result" => "ok");
 
 787         if(!is_null($json)) {
 
 789                         if($playlist_empty&&$something_added) {
 
 790                                 $play = get_playback();
 
 794                         $json["status"] = $pl->getStatus();
 
 795                         if(isset($_GET['plchanges'])&&is_numeric(trim($_GET['plchanges']))&&$_GET['plchanges']!=$json['status']['playlist']) {
 
 796                                 $res = $pl->getChanges(trim($_GET['plchanges']), true);
 
 797                                 if($res&&isset($res['file'])&&is_array($res['file'])) {
 
 799                                         if(isset($_GET['pmax'])&&isset($_GET['page'])) {
 
 801                                                 $max = intval($_GET['pmax']);
 
 802                                                 $page = intval($_GET['page']);
 
 803                                                 $start = $max * $page;
 
 804                                                 $end = $start + $max;
 
 806                                                 foreach($res['file'] as $f) {
 
 807                                                         if($f['cpos']>=$start&&$f['cpos']<$end)
 
 813                                                         $json['plchanges'] = &$ret;
 
 817                                                 $json["plchanges"] = &$res['file'];
 
 823                 catch(PEAR_Exception $e) {
 
 825                 array_to_json($json);
 
 830                 } catch(PEAR_Exception $e) {}