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'])&&$rw) {
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'])&&$rw) {
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'])&&$rw) {
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']))&&$rw) {
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'])) && $rw) {
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']) && $rw) {
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']) && $rw) {
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']) && $rw) {
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']) && $rw) {
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']) && $rw) {
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'])&&$rw) {
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'])&&$rw) {
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'])&&$rw) {
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'])&&$rw) {
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'])&&$rw) {
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'])&&$rw) {
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) {}