From: Roman Bazalevsky Date: Wed, 28 Sep 2016 16:50:21 +0000 (+0300) Subject: Веб-интерфейс к MPD. X-Git-Url: https://git.rvb.name/mpd-web.git/commitdiff_plain/e05325ceb8b812fc40b7c3475c59602dde9efdae Веб-интерфейс к MPD. Серверная часть - PHP + libmpc. Клиентская часть - AJAX без библиотек. --- e05325ceb8b812fc40b7c3475c59602dde9efdae diff --git a/ajax/command.php b/ajax/command.php new file mode 100644 index 0000000..25567a4 --- /dev/null +++ b/ajax/command.php @@ -0,0 +1,8 @@ + diff --git a/ajax/editplaylist.php b/ajax/editplaylist.php new file mode 100644 index 0000000..b7e8bf4 --- /dev/null +++ b/ajax/editplaylist.php @@ -0,0 +1,72 @@ + '\\\'', + "\\" => '\\\\', + "\r\n" => "\\r\\n", + "\n" => "\\n" + ); + return strtr($target, $replacements); +} + +include('mpd.php'); +$dir = $_REQUEST['dir']; + +?> +
+ + + +
+ + +
+
+
+ + + + + GetDir($dir); + ?> + + + + "> + + + + + + + + + +
TitleControl
+
+ +
+ + + +
+ + +
+
diff --git a/ajax/mpd.js b/ajax/mpd.js new file mode 100644 index 0000000..5d64a92 --- /dev/null +++ b/ajax/mpd.js @@ -0,0 +1,198 @@ + +function RefreshNowPlaying() { + +var req = new XMLHttpRequest(); + +req.onreadystatechange = function () { + if (this.readyState != 4 || this.status != 200) return; + document.getElementById('nowplaying_content').innerHTML=this.responseText; + document.title='MPD Player: '+this.responseText; +}; + +req.open("GET", "ajax/trackinfo.php", true); +req.send(); + +} + +function RefreshPlayerState() { + +var req = new XMLHttpRequest(); + +req.onreadystatechange = function () { + if (this.readyState != 4 || this.status != 200) return; + if (this.responseText=="play") { + document.getElementById('playpausebutton').innerHTML=""; + } else { + document.getElementById('playpausebutton').innerHTML=""; + } +}; + +req.open("GET", "ajax/playerstate.php", true); +req.send(); + +} + +function RefreshRepeatState() { + +var req = new XMLHttpRequest(); + +req.onreadystatechange = function () { + if (this.readyState != 4 || this.status != 200) return; + if (this.responseText=="1") { + document.getElementById('repeatstate').innerHTML=""; + } else { + document.getElementById('repeatstate').innerHTML=""; + } +}; + +req.open("GET", "ajax/repeatstate.php", true); +req.send(); + +} + +function RefreshVolume() { + +var req = new XMLHttpRequest(); + +req.onreadystatechange = function () { + if (this.readyState != 4 || this.status != 200) return; + document.getElementById('volume_total').innerHTML="
"; +}; + +req.open("GET", "ajax/volume.php", true); +req.send(); + +} + +function RefreshPageStatus() { + + RefreshNowPlaying(); + RefreshPlayerState(); + RefreshRepeatState(); + RefreshVolume(); + +} + +function RefreshPlaylist() { + +var req = new XMLHttpRequest(); + +req.onreadystatechange = function () { + if (this.readyState != 4 || this.status != 200) return; + document.getElementById('playlist').innerHTML=this.responseText; +}; + +req.open("GET", "ajax/playlist.php", true); +req.send(); + +} + +function EditPlayList(dir) { + +var req = new XMLHttpRequest(); + +req.onreadystatechange = function () { + if (this.readyState != 4 || this.status != 200) return; + document.getElementById('playlist').innerHTML=this.responseText; +}; + +if (!dir) { dir = ''; }; + +req.open("GET", "ajax/editplaylist.php?dir="+dir, true); +req.send(); + +} + +function RefreshPageContent() { + + RefreshPageStatus(); + RefreshPlaylist(); + +} + +function Command(cmd) { + +var req = new XMLHttpRequest(); + +req.onreadystatechange = function () { + if (this.readyState != 4 || this.status != 200) return; + RefreshPageStatus(); +}; + +req.open("GET", "ajax/command.php?task="+cmd, true); +req.send(); + +} + +function PlaylistCommand(cmd,item) { + +var req = new XMLHttpRequest(); + +req.onreadystatechange = function () { + if (this.readyState != 4 || this.status != 200) return; + RefreshPageContent(); +}; + +req.open("GET", "ajax/playlist-command.php?item="+item+"&task="+cmd, true); +req.send(); + +} + +function PlaylistEditCommand(cmd,item) { + +var req = new XMLHttpRequest(); + +req.open("GET", "ajax/playlist-command.php?item="+item+"&task="+cmd, true); +req.send(); + +} + + +function PlaylistItemsCommand(cmd) { + +var req = new XMLHttpRequest(); + +var selected = [].filter.call(document.getElementsByName('itemlist[]'), function(c) { + return c.checked; +}).map(function(c) { + return c.value; +}); + +req.onreadystatechange = function () { + if (this.readyState != 4 || this.status != 200) return; + RefreshPageContent(); +}; + +params=selected.map(function(el) { + //Map each field into a name=value string, make sure to properly escape! + return 'itemlist[]=' + encodeURIComponent(el); + }).join('&'); + +req.open("POST", "ajax/playlist-command.php?task="+cmd, true); +req.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); +req.send(params); + +} + +function PlaylistEditItemsCommand(cmd,dir) { + +var req = new XMLHttpRequest(); + +var selected = [].filter.call(document.getElementsByName('itemlist[]'), function(c) { + return c.checked; +}).map(function(c) { + return c.value; +}); + +params=selected.map(function(el) { + //Map each field into a name=value string, make sure to properly escape! + return 'itemlist[]=' + encodeURIComponent(el); + }).join('&'); + +req.open("POST", "ajax/playlist-command.php?dir="+dir+"&task="+cmd, true); +req.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); +req.send(params); + +} + +// setInterval(RefreshPageStatus, 5000); diff --git a/ajax/mpd.php b/ajax/mpd.php new file mode 100644 index 0000000..9f435e2 --- /dev/null +++ b/ajax/mpd.php @@ -0,0 +1,14 @@ +connected != 1){ + echo "Not Connected to Server"; + exit(1); +} + +?> diff --git a/ajax/playerstate.php b/ajax/playerstate.php new file mode 100644 index 0000000..c4735a3 --- /dev/null +++ b/ajax/playerstate.php @@ -0,0 +1,7 @@ +state; + +?> diff --git a/ajax/playlist-command.php b/ajax/playlist-command.php new file mode 100644 index 0000000..19344da --- /dev/null +++ b/ajax/playlist-command.php @@ -0,0 +1,9 @@ + diff --git a/ajax/playlist.php b/ajax/playlist.php new file mode 100644 index 0000000..78fe8ad --- /dev/null +++ b/ajax/playlist.php @@ -0,0 +1,50 @@ +current_track_id; +$playlist= $mpd->playlist; +?> +
+ +
+ + + + + + "> + + + + + + + + + +
TitleControls
+ + + + + + + + + + +
+
+ +
diff --git a/ajax/repeatstate.php b/ajax/repeatstate.php new file mode 100644 index 0000000..c983152 --- /dev/null +++ b/ajax/repeatstate.php @@ -0,0 +1,7 @@ +repeat; + +?> diff --git a/ajax/trackinfo.php b/ajax/trackinfo.php new file mode 100644 index 0000000..e5cc464 --- /dev/null +++ b/ajax/trackinfo.php @@ -0,0 +1,17 @@ +current_track_id; +$playlist= $mpd->playlist; +if($cur_track !=-1) + + if ($playlist[$cur_track]['name']==$playlist[$cur_track]['title']) { + echo htmlentities(cut_file($playlist[$cur_track]['name'])); + } else { + echo htmlentities($playlist[$cur_track]['title']); + } +else + echo "No Track selected"; + +?> diff --git a/ajax/volume.php b/ajax/volume.php new file mode 100644 index 0000000..bd3caf0 --- /dev/null +++ b/ajax/volume.php @@ -0,0 +1,7 @@ +volume; + +?> diff --git a/config/config.php b/config/config.php new file mode 100644 index 0000000..fecc1f6 --- /dev/null +++ b/config/config.php @@ -0,0 +1,7 @@ + diff --git a/css/general.css b/css/general.css new file mode 100644 index 0000000..19078d4 --- /dev/null +++ b/css/general.css @@ -0,0 +1,192 @@ +/* +#525356 +*/ + + + +* { + margin:0; +} + +body { + text-align: center; + font-family:arial,sans-serif; + margin-top: 15px; + margin-bottom: 15px; + color:#AEAEAE; +} + +a { + text-decoration:none; + color: #AEAEAE; +} + +table +{ + border-style:none; + padding:0px; + border-collapse: collapse; + border-spacing: 0; + +} + +img{ + border:0px; + margin: 2px 2px 2px 2px; +} + +button +{ +background : none; +cursor : pointer; +border: 0px; +margin : 0px; +padding : 0px; +} + +.button +{ +cursor : pointer; +} + +#heading_tbl { + width: 400px +} + +#frame { + text-align: left; + border: 1px solid #000; + width: 400px; + background:url(../images/bg.png) no-repeat #251616; + margin: 0 auto; +} + + +#nowplaying_heading { + font-size: 1.3em; + font-wieght: bold; + height: 30px; + background: url(../images/control_bg.png) repeat-x; + border-bottom: 1px solid #525356; +} + +#nowplaying_content { + margin: 10px 5px 10px 5px; + text-align: center; + border: 1px solid #525356; + width: 380px; + padding: 30px 0px 30px 0px; + background:url(../images/playing_bg.png); + font-weight: bold; + color: #fff; +} + +#control_buttons { + text-align:center; + width: 390px; + margin: 10px 5px 10px 5px; +} + +#control_buttons table { + margin-left:auto; + margin-right:auto; +} + + +#control_volume { + text-align:center; + width: 390px; + margin: 10px 5px 10px 5px; +} + +#control_volume table { + margin-left:auto; + margin-right:auto; +} + +#playlist_menu { + font-wieght: bold; + height: 24px; + background: url(../images/control_bg.png) repeat-x; + border-top: 2px solid #525356; + margin-top:5px; + margin-bottom:5px; +} + +#items_heading { + text-align: center; + font-size: 1.0em; + font-weight:bold; + border-bottom: 1px solid #525356; + border-top: 1px solid #525356; + background-color: #333; +} + +#home { + text-align: left; + font-size: 1.0em; + font-weight:bold; + border-bottom: 3px solid #525356; + background-color: #251616; +} + +#items { + font-size: 0.9em; + text-align: center; + min-height: 400px; + background:url(../images/playing_bg.png); +} + +#items table { + width: 390px; + margin: 0px 5px 0px 5px; + border: 1px solid #525356; +} + +#file { + text-align: left; + width: 100%; + +} + + +#itemEven{ + background-color: #251616; + +} + +#itemOdd{ + background-color: #333; +} + +#itemActive { + background: url(../images/playing_bg.png); + color: #fff; +} + +#itemActive a { + color: #fff; +} + + +#move img { + margin: 1px 1px 1px 1px; +} + +#remove img { + margin: 1px 1px 1px 1px; +} + + +#volume_total { + width: 100px; + border: 1px solid #525356; + height: 5px; +} + + +#volume_actual { + background-color: #d9600c; + height:5px; +} + diff --git a/images/addall.png b/images/addall.png new file mode 100755 index 0000000..fa3983e Binary files /dev/null and b/images/addall.png differ diff --git a/images/addselected.png b/images/addselected.png new file mode 100755 index 0000000..52fbb78 Binary files /dev/null and b/images/addselected.png differ diff --git a/images/bg.png b/images/bg.png new file mode 100755 index 0000000..5bd1ec8 Binary files /dev/null and b/images/bg.png differ diff --git a/images/control_bg.png b/images/control_bg.png new file mode 100755 index 0000000..685abbb Binary files /dev/null and b/images/control_bg.png differ diff --git a/images/down.png b/images/down.png new file mode 100755 index 0000000..e8f3d6e Binary files /dev/null and b/images/down.png differ diff --git a/images/highlight.png b/images/highlight.png new file mode 100755 index 0000000..3710d71 Binary files /dev/null and b/images/highlight.png differ diff --git a/images/left.png b/images/left.png new file mode 100755 index 0000000..00714b7 Binary files /dev/null and b/images/left.png differ diff --git a/images/minus.png b/images/minus.png new file mode 100755 index 0000000..dff37f6 Binary files /dev/null and b/images/minus.png differ diff --git a/images/next.png b/images/next.png new file mode 100755 index 0000000..fc2c791 Binary files /dev/null and b/images/next.png differ diff --git a/images/pause.png b/images/pause.png new file mode 100755 index 0000000..9508f98 Binary files /dev/null and b/images/pause.png differ diff --git a/images/play.png b/images/play.png new file mode 100755 index 0000000..3591973 Binary files /dev/null and b/images/play.png differ diff --git a/images/playing_bg.png b/images/playing_bg.png new file mode 100755 index 0000000..f2e2337 Binary files /dev/null and b/images/playing_bg.png differ diff --git a/images/playlist.png b/images/playlist.png new file mode 100755 index 0000000..ae4af14 Binary files /dev/null and b/images/playlist.png differ diff --git a/images/plus.png b/images/plus.png new file mode 100755 index 0000000..b72d579 Binary files /dev/null and b/images/plus.png differ diff --git a/images/prev.png b/images/prev.png new file mode 100755 index 0000000..aedbbdf Binary files /dev/null and b/images/prev.png differ diff --git a/images/previous.png b/images/previous.png new file mode 100755 index 0000000..9c49139 Binary files /dev/null and b/images/previous.png differ diff --git a/images/remall.png b/images/remall.png new file mode 100755 index 0000000..ffb546a Binary files /dev/null and b/images/remall.png differ diff --git a/images/remove.png b/images/remove.png new file mode 100755 index 0000000..bf94562 Binary files /dev/null and b/images/remove.png differ diff --git a/images/removeall.png b/images/removeall.png new file mode 100755 index 0000000..4fe02d1 Binary files /dev/null and b/images/removeall.png differ diff --git a/images/removeselected.png b/images/removeselected.png new file mode 100755 index 0000000..35294aa Binary files /dev/null and b/images/removeselected.png differ diff --git a/images/remselected.png b/images/remselected.png new file mode 100755 index 0000000..6466dce Binary files /dev/null and b/images/remselected.png differ diff --git a/images/repeatoff.png b/images/repeatoff.png new file mode 100755 index 0000000..0a17659 Binary files /dev/null and b/images/repeatoff.png differ diff --git a/images/repeaton.png b/images/repeaton.png new file mode 100755 index 0000000..aaef8ce Binary files /dev/null and b/images/repeaton.png differ diff --git a/images/right.png b/images/right.png new file mode 100755 index 0000000..2ce6d36 Binary files /dev/null and b/images/right.png differ diff --git a/images/songs.png b/images/songs.png new file mode 100755 index 0000000..f24c1ef Binary files /dev/null and b/images/songs.png differ diff --git a/images/stop.png b/images/stop.png new file mode 100755 index 0000000..1e1dd0f Binary files /dev/null and b/images/stop.png differ diff --git a/images/up.png b/images/up.png new file mode 100755 index 0000000..0ada34a Binary files /dev/null and b/images/up.png differ diff --git a/images/update.png b/images/update.png new file mode 100755 index 0000000..fa2242b Binary files /dev/null and b/images/update.png differ diff --git a/index.php b/index.php new file mode 100644 index 0000000..40c64d8 --- /dev/null +++ b/index.php @@ -0,0 +1,17 @@ +connected != 1){ + echo "Not Connected to Server"; + exit(1); +} + +$task=$_REQUEST['task']; + +include('view/default/view.php'); + +?> diff --git a/model/player.php b/model/player.php new file mode 100644 index 0000000..ff3fc0d --- /dev/null +++ b/model/player.php @@ -0,0 +1,44 @@ +Play(); + break; + + case("prev"): + $mpd->Previous(); + break; + + case("pause"): + $mpd->Pause(); + break; + + case("stop"): + $mpd->Stop(); + break; + + case("next"): + $mpd->Next(); + break; + + case("vold"): + $mpd->AdjustVolume(-3); + break; + case("volu"): + $mpd->AdjustVolume(+3); + break; + + case("update"): + $mpd->DBRefresh(); + break; + + case("repeat"): + $repeat = $mpd->repeat; + if($repeat==1) + $mpd->SetRepeat(0); + else + $mpd->SetRepeat(1); + break; +} +?> diff --git a/model/tracklist.php b/model/tracklist.php new file mode 100644 index 0000000..798ca01 --- /dev/null +++ b/model/tracklist.php @@ -0,0 +1,100 @@ +GetDir($current); + for($i=0;$iPLAdd($files[$i]['name']); + } + + } + } +} + +switch($task) { + + case("remove"): + if($item != null) + $mpd->PLRemove($item); + break; + + + case("clear"): + $mpd->PLClear(); + break; + + case("playitem"): + if($item != null){ + $mpd->SkipTo($item); + } + break; + + case("addfile"): + $mpd->PLAdd($item); + break; + + case("adddir"): + addDir($item,$mpd); + break; + + + case("moveup"): + if($item !=0) { + $mpd->Move($item,$item-1); + } + break; + + case("movedown"): + if($item !=count($mpd->playlist)-1) { + $mpd->Move($item,$item+1); + } + break; + + + case("removeselected"): + $items=$_POST['itemlist']; + for($i=0;$iPLRemove($items[$i]-$i); + } + break; + + + case("addselected"): + $items=$_POST['itemlist']; + $dir=$_REQUEST['dir']; + $files = $mpd->GetDir($dir); + for($i=0;$iPLAdd($files[$items[$i]]['name']); + } + } + break; + + case("addall"): + $dir=$_REQUEST['item']; + $files = $mpd->GetDir($dir); + for($i=0;$iPLAdd($files[$i]['name']); + } + } + break; + + + +} + + +?> diff --git a/system/helper.php b/system/helper.php new file mode 100644 index 0000000..10009e4 --- /dev/null +++ b/system/helper.php @@ -0,0 +1,35 @@ + diff --git a/system/mpd_class.php b/system/mpd_class.php new file mode 100644 index 0000000..8ffddcd --- /dev/null +++ b/system/mpd_class.php @@ -0,0 +1,976 @@ +host = $srv; + $this->port = $port; + $this->password = $pwd; + + $resp = $this->Connect(); + if ( is_null($resp) ) { + $this->errStr = "Could not connect"; + return; + } else { + list ( $this->mpd_version ) = sscanf($resp, MPD_RESPONSE_OK . " MPD %s\n"); + if ( ! is_null($pwd) ) { + if ( is_null($this->SendCommand(MPD_CMD_PASSWORD,$pwd)) ) { + $this->connected = FALSE; + return; // bad password or command + } + if ( is_null($this->RefreshInfo()) ) { // no read access -- might as well be disconnected! + $this->connected = FALSE; + $this->errStr = "Password supplied does not have read access"; + return; + } + } else { + if ( is_null($this->RefreshInfo()) ) { // no read access -- might as well be disconnected! + $this->connected = FALSE; + $this->errStr = "Password required to access server"; + return; + } + } + } + } + + /* Connect() + * + * Connects to the MPD server. + * + * NOTE: This is called automatically upon object instantiation; you should not need to call this directly. + */ + function Connect() { + if ( $this->debugging ) echo "mpd->Connect() / host: ".$this->host.", port: ".$this->port."\n"; + $this->mpd_sock = fsockopen($this->host,$this->port,$errNo,$errStr,10); + if (!$this->mpd_sock) { + $this->errStr = "Socket Error: $errStr ($errNo)"; + return NULL; + } else { + while(!feof($this->mpd_sock)) { + $response = fgets($this->mpd_sock,1024); + if (strncmp(MPD_RESPONSE_OK,$response,strlen(MPD_RESPONSE_OK)) == 0) { + $this->connected = TRUE; + return $response; + break; + } + if (strncmp(MPD_RESPONSE_ERR,$response,strlen(MPD_RESPONSE_ERR)) == 0) { + $this->errStr = "Server responded with: $response"; + return NULL; + } + } + // Generic response + $this->errStr = "Connection not available"; + return NULL; + } + } + + /* SendCommand() + * + * Sends a generic command to the MPD server. Several command constants are pre-defined for + * use (see MPD_CMD_* constant definitions above). + */ + function SendCommand($cmdStr,$arg1 = "",$arg2 = "") { + if ( $this->debugging ) echo "mpd->SendCommand() / cmd: ".$cmdStr.", args: ".$arg1." ".$arg2."\n"; + + if ( ! $this->connected ) { + echo "mpd->SendCommand() / Error: Not connected\n"; + } else { + // Clear out the error String + $this->errStr = ""; + $respStr = ""; + + // Check the command compatibility: + if ( ! $this->_checkCompatibility($cmdStr) ) { + echo "Not compatible command!"; + return NULL; + } + + if (strlen($arg1) > 0) $cmdStr .= " \"$arg1\""; + + if (strlen($arg2) > 0) $cmdStr .= " \"$arg2\""; + if ( $this->debugging ) echo "mpd-> ".$cmdStr."\n"; + fputs($this->mpd_sock,"$cmdStr\n"); + while(!feof($this->mpd_sock)) { + $response = fgets($this->mpd_sock,1024); + if ( $this->debugging ) echo "mpd.response-> ".$response."\n"; + + // An OK signals the end of transmission -- we'll ignore it + if (strncmp(MPD_RESPONSE_OK,$response,strlen(MPD_RESPONSE_OK)) == 0) { + break; + } + + // An ERR signals the end of transmission with an error! Let's grab the single-line message. + if (strncmp(MPD_RESPONSE_ERR,$response,strlen(MPD_RESPONSE_ERR)) == 0) { + list ( $junk, $errTmp ) = explode(MPD_RESPONSE_ERR . " ",$response ); + $this->errStr = strtok($errTmp,"\n"); + } + + if ( strlen($this->errStr) > 0 ) { + return NULL; + } + + // Build the response string + $respStr .= $response; + } + if ( $this->debugging ) echo "mpd->SendCommand() / response: '".$respStr."'\n"; + } + return $respStr; + } + + /* QueueCommand() + * + * Queues a generic command for later sending to the MPD server. The CommandQueue can hold + * as many commands as needed, and are sent all at once, in the order they are queued, using + * the SendCommandQueue() method. The syntax for queueing commands is identical to SendCommand(). + */ + function QueueCommand($cmdStr,$arg1 = "",$arg2 = "") { + if ( $this->debugging ) echo "mpd->QueueCommand() / cmd: ".$cmdStr.", args: ".$arg1." ".$arg2."\n"; + if ( ! $this->connected ) { + echo "mpd->QueueCommand() / Error: Not connected\n"; + return NULL; + } else { + if ( strlen($this->command_queue) == 0 ) { + $this->command_queue = MPD_CMD_START_BULK . "\n"; + } + if (strlen($arg1) > 0) $cmdStr .= " \"$arg1\""; + if (strlen($arg2) > 0) $cmdStr .= " \"$arg2\""; + + $this->command_queue .= $cmdStr ."\n"; + + if ( $this->debugging ) echo "mpd->QueueCommand() / return\n"; + } + return TRUE; + } + + /* SendCommandQueue() + * + * Sends all commands in the Command Queue to the MPD server. See also QueueCommand(). + */ + function SendCommandQueue() { + if ( $this->debugging ) echo "mpd->SendCommandQueue()\n"; + if ( ! $this->connected ) { + echo "mpd->SendCommandQueue() / Error: Not connected\n"; + return NULL; + } else { + $this->command_queue .= MPD_CMD_END_BULK . "\n"; + if ( is_null($respStr = $this->SendCommand($this->command_queue)) ) { + return NULL; + } else { + $this->command_queue = NULL; + if ( $this->debugging ) echo "mpd->SendCommandQueue() / response: '".$respStr."'\n"; + } + } + return $respStr; + } + + /* AdjustVolume() + * + * Adjusts the mixer volume on the MPD by , which can be a positive (volume increase), + * or negative (volume decrease) value. + */ + function AdjustVolume($modifier) { + if ( $this->debugging ) echo "mpd->AdjustVolume()\n"; + if ( ! is_numeric($modifier) ) { + $this->errStr = "AdjustVolume() : argument 1 must be a numeric value"; + return NULL; + } + + $this->RefreshInfo(); + $newVol = $this->volume + $modifier; + $ret = $this->SetVolume($newVol); + + if ( $this->debugging ) echo "mpd->AdjustVolume() / return\n"; + return $ret; + } + + /* SetVolume() + * + * Sets the mixer volume to , which should be between 1 - 100. + */ + function SetVolume($newVol) { + if ( $this->debugging ) echo "mpd->SetVolume()\n"; + if ( ! is_numeric($newVol) ) { + $this->errStr = "SetVolume() : argument 1 must be a numeric value"; + return NULL; + } + + // Forcibly prevent out of range errors + if ( $newVol < 0 ) $newVol = 0; + if ( $newVol > 100 ) $newVol = 100; + + // If we're not compatible with SETVOL, we'll try adjusting using VOLUME + if ( $this->_checkCompatibility(MPD_CMD_SETVOL) ) { + if ( ! is_null($ret = $this->SendCommand(MPD_CMD_SETVOL,$newVol))) $this->volume = $newVol; + } else { + $this->RefreshInfo(); // Get the latest volume + if ( is_null($this->volume) ) { + return NULL; + } else { + $modifier = ( $newVol - $this->volume ); + if ( ! is_null($ret = $this->SendCommand(MPD_CMD_VOLUME,$modifier))) $this->volume = $newVol; + } + } + + if ( $this->debugging ) echo "mpd->SetVolume() / return\n"; + return $ret; + } + + /* GetDir() + * + * Retrieves a database directory listing of the directory and places the results into + * a multidimensional array. If no directory is specified, the directory listing is at the + * base of the MPD music path. + */ + function GetDir($dir = "") { + if ( $this->debugging ) echo "mpd->GetDir()\n"; + $resp = $this->SendCommand(MPD_CMD_LSDIR,$dir); + $dirlist = $this->_parseFileListResponse($resp); + if ( $this->debugging ) echo "mpd->GetDir() / return ".print_r($dirlist)."\n"; + return $dirlist; + } + + /* PLAdd() + * + * Adds each track listed in a single-dimensional , which contains filenames + * of tracks to add, to the end of the playlist. This is used to add many, many tracks to + * the playlist in one swoop. + */ + function PLAddBulk($trackArray) { + if ( $this->debugging ) echo "mpd->PLAddBulk()\n"; + $num_files = count($trackArray); + for ( $i = 0; $i < $num_files; $i++ ) { + $this->QueueCommand(MPD_CMD_PLADD,$trackArray[$i]); + } + $resp = $this->SendCommandQueue(); + $this->RefreshInfo(); + if ( $this->debugging ) echo "mpd->PLAddBulk() / return\n"; + return $resp; + } + + /* PLAdd() + * + * Adds the file to the end of the playlist. must be a track in the MPD database. + */ + function PLAdd($fileName) { + if ( $this->debugging ) echo "mpd->PLAdd()\n"; + if ( ! is_null($resp = $this->SendCommand(MPD_CMD_PLADD,$fileName))) $this->RefreshInfo(); + if ( $this->debugging ) echo "mpd->PLAdd() / return\n"; + return $resp; + } + + /* PLMoveTrack() + * + * Moves track number to position in the playlist. This is used to reorder + * the songs in the playlist. + */ + function PLMoveTrack($origPos, $newPos) { + if ( $this->debugging ) echo "mpd->PLMoveTrack()\n"; + if ( ! is_numeric($origPos) ) { + $this->errStr = "PLMoveTrack(): argument 1 must be numeric"; + return NULL; + } + if ( $origPos < 0 or $origPos > $this->playlist_count ) { + $this->errStr = "PLMoveTrack(): argument 1 out of range"; + return NULL; + } + if ( $newPos < 0 ) $newPos = 0; + if ( $newPos > $this->playlist_count ) $newPos = $this->playlist_count; + + if ( ! is_null($resp = $this->SendCommand(MPD_CMD_PLMOVETRACK,$origPos,$newPos))) $this->RefreshInfo(); + if ( $this->debugging ) echo "mpd->PLMoveTrack() / return\n"; + return $resp; + } + + /* PLShuffle() + * + * Randomly reorders the songs in the playlist. + */ + function PLShuffle() { + if ( $this->debugging ) echo "mpd->PLShuffle()\n"; + if ( ! is_null($resp = $this->SendCommand(MPD_CMD_PLSHUFFLE))) $this->RefreshInfo(); + if ( $this->debugging ) echo "mpd->PLShuffle() / return\n"; + return $resp; + } + + /* PLLoad() + * + * Retrieves the playlist from .m3u and loads it into the current playlist. + */ + function PLLoad($file) { + if ( $this->debugging ) echo "mpd->PLLoad()\n"; + if ( ! is_null($resp = $this->SendCommand(MPD_CMD_PLLOAD,$file))) $this->RefreshInfo(); + if ( $this->debugging ) echo "mpd->PLLoad() / return\n"; + return $resp; + } + + /* PLSave() + * + * Saves the playlist to .m3u for later retrieval. The file is saved in the MPD playlist + * directory. + */ + function PLSave($file) { + if ( $this->debugging ) echo "mpd->PLSave()\n"; + $resp = $this->SendCommand(MPD_CMD_PLSAVE,$file); + if ( $this->debugging ) echo "mpd->PLSave() / return\n"; + return $resp; + } + + /* PLClear() + * + * Empties the playlist. + */ + function PLClear() { + if ( $this->debugging ) echo "mpd->PLClear()\n"; + if ( ! is_null($resp = $this->SendCommand(MPD_CMD_PLCLEAR))) $this->RefreshInfo(); + if ( $this->debugging ) echo "mpd->PLClear() / return\n"; + return $resp; + } + + + /* PLMove() + * + * Move song from pos to the other + */ + function Move ($from,$to) { + if ( ! is_null($resp = $this->SendCommand(MPD_CMD_PLMOVETRACK,$from,$to))) $this->RefreshInfo(); + //die(print_r($resp)); + + } + + + /* PLRemove() + * + * Removes track from the playlist. + */ + function PLRemove($id) { + if ( $this->debugging ) echo "mpd->PLRemove()\n"; + if ( ! is_numeric($id) ) { + $this->errStr = "PLRemove() : argument 1 must be a numeric value"; + return NULL; + } + if ( ! is_null($resp = $this->SendCommand(MPD_CMD_PLREMOVE,$id))) $this->RefreshInfo(); + if ( $this->debugging ) echo "mpd->PLRemove() / return\n"; + return $resp; + } + + /* SetRepeat() + * + * Enables 'loop' mode -- tells MPD continually loop the playlist. The parameter + * is either 1 (on) or 0 (off). + */ + function SetRepeat($repVal) { + if ( $this->debugging ) echo "mpd->SetRepeat()\n"; + $rpt = $this->SendCommand(MPD_CMD_REPEAT,$repVal); + $this->repeat = $repVal; + if ( $this->debugging ) echo "mpd->SetRepeat() / return\n"; + return $rpt; + } + + /* SetRandom() + * + * Enables 'randomize' mode -- tells MPD to play songs in the playlist in random order. The + * parameter is either 1 (on) or 0 (off). + */ + function SetRandom($rndVal) { + if ( $this->debugging ) echo "mpd->SetRandom()\n"; + $resp = $this->SendCommand(MPD_CMD_RANDOM,$rndVal); + $this->random = $rndVal; + if ( $this->debugging ) echo "mpd->SetRandom() / return\n"; + return $resp; + } + + /* Shutdown() + * + * Shuts down the MPD server (aka sends the KILL command). This closes the current connection, + * and prevents future communication with the server. + */ + function Shutdown() { + if ( $this->debugging ) echo "mpd->Shutdown()\n"; + $resp = $this->SendCommand(MPD_CMD_SHUTDOWN); + + $this->connected = FALSE; + unset($this->mpd_version); + unset($this->errStr); + unset($this->mpd_sock); + + if ( $this->debugging ) echo "mpd->Shutdown() / return\n"; + return $resp; + } + + /* DBRefresh() + * + * Tells MPD to rescan the music directory for new tracks, and to refresh the Database. Tracks + * cannot be played unless they are in the MPD database. + */ + function DBRefresh() { + if ( $this->debugging ) echo "mpd->DBRefresh()\n"; + $resp = $this->SendCommand(MPD_CMD_REFRESH); + + // Update local variables + $this->RefreshInfo(); + + if ( $this->debugging ) echo "mpd->DBRefresh() / return\n"; + return $resp; + } + + /* Play() + * + * Begins playing the songs in the MPD playlist. + */ + function Play() { + if ( $this->debugging ) echo "mpd->Play()\n"; + if ( ! is_null($rpt = $this->SendCommand(MPD_CMD_PLAY) )) $this->RefreshInfo(); + if ( $this->debugging ) echo "mpd->Play() / return\n"; + return $rpt; + } + + /* Stop() + * + * Stops playing the MPD. + */ + function Stop() { + if ( $this->debugging ) echo "mpd->Stop()\n"; + if ( ! is_null($rpt = $this->SendCommand(MPD_CMD_STOP) )) $this->RefreshInfo(); + if ( $this->debugging ) echo "mpd->Stop() / return\n"; + return $rpt; + } + + /* Pause() + * + * Toggles pausing on the MPD. Calling it once will pause the player, calling it again + * will unpause. + */ + function Pause() { + if ( $this->debugging ) echo "mpd->Pause()\n"; + if ( ! is_null($rpt = $this->SendCommand(MPD_CMD_PAUSE) )) $this->RefreshInfo(); + if ( $this->debugging ) echo "mpd->Pause() / return\n"; + return $rpt; + } + + /* SeekTo() + * + * Skips directly to the song in the MPD playlist. + */ + function SkipTo($idx) { + if ( $this->debugging ) echo "mpd->SkipTo()\n"; + if ( ! is_numeric($idx) ) { + $this->errStr = "SkipTo() : argument 1 must be a numeric value"; + return NULL; + } + if ( ! is_null($rpt = $this->SendCommand(MPD_CMD_PLAY,$idx))) $this->RefreshInfo(); + if ( $this->debugging ) echo "mpd->SkipTo() / return\n"; + return $idx; + } + + /* SeekTo() + * + * Skips directly to a given position within a track in the MPD playlist. The argument, + * given in seconds, is the track position to locate. The argument, if supplied is + * the track number in the playlist. If is not specified, the current track is assumed. + */ + function SeekTo($pos, $track = -1) { + if ( $this->debugging ) echo "mpd->SeekTo()\n"; + if ( ! is_numeric($pos) ) { + $this->errStr = "SeekTo() : argument 1 must be a numeric value"; + return NULL; + } + if ( ! is_numeric($track) ) { + $this->errStr = "SeekTo() : argument 2 must be a numeric value"; + return NULL; + } + if ( $track == -1 ) { + $track = $this->current_track_id; + } + + if ( ! is_null($rpt = $this->SendCommand(MPD_CMD_SEEK,$track,$pos))) $this->RefreshInfo(); + if ( $this->debugging ) echo "mpd->SeekTo() / return\n"; + return $pos; + } + + /* Next() + * + * Skips to the next song in the MPD playlist. If not playing, returns an error. + */ + function Next() { + if ( $this->debugging ) echo "mpd->Next()\n"; + if ( ! is_null($rpt = $this->SendCommand(MPD_CMD_NEXT))) $this->RefreshInfo(); + if ( $this->debugging ) echo "mpd->Next() / return\n"; + return $rpt; + } + + /* Previous() + * + * Skips to the previous song in the MPD playlist. If not playing, returns an error. + */ + function Previous() { + if ( $this->debugging ) echo "mpd->Previous()\n"; + if ( ! is_null($rpt = $this->SendCommand(MPD_CMD_PREV))) $this->RefreshInfo(); + if ( $this->debugging ) echo "mpd->Previous() / return\n"; + return $rpt; + } + + /* Search() + * + * Searches the MPD database. The search should be one of the following: + * MPD_SEARCH_ARTIST, MPD_SEARCH_TITLE, MPD_SEARCH_ALBUM + * The search is a case-insensitive locator string. Anything that contains + * will be returned in the results. + */ + function Search($type,$string) { + if ( $this->debugging ) echo "mpd->Search()\n"; + if ( $type != MPD_SEARCH_ARTIST and + $type != MPD_SEARCH_ALBUM and + $type != MPD_SEARCH_TITLE ) { + $this->errStr = "mpd->Search(): invalid search type"; + return NULL; + } else { + if ( is_null($resp = $this->SendCommand(MPD_CMD_SEARCH,$type,$string))) return NULL; + $searchlist = $this->_parseFileListResponse($resp); + } + if ( $this->debugging ) echo "mpd->Search() / return ".print_r($searchlist)."\n"; + return $searchlist; + } + + /* Find() + * + * Find() looks for exact matches in the MPD database. The find should be one of + * the following: + * MPD_SEARCH_ARTIST, MPD_SEARCH_TITLE, MPD_SEARCH_ALBUM + * The find is a case-insensitive locator string. Anything that exactly matches + * will be returned in the results. + */ + function Find($type,$string) { + if ( $this->debugging ) echo "mpd->Find()\n"; + if ( $type != MPD_SEARCH_ARTIST and + $type != MPD_SEARCH_ALBUM and + $type != MPD_SEARCH_TITLE ) { + $this->errStr = "mpd->Find(): invalid find type"; + return NULL; + } else { + if ( is_null($resp = $this->SendCommand(MPD_CMD_FIND,$type,$string))) return NULL; + $searchlist = $this->_parseFileListResponse($resp); + } + if ( $this->debugging ) echo "mpd->Find() / return ".print_r($searchlist)."\n"; + return $searchlist; + } + + /* Disconnect() + * + * Closes the connection to the MPD server. + */ + function Disconnect() { + if ( $this->debugging ) echo "mpd->Disconnect()\n"; + fclose($this->mpd_sock); + + $this->connected = FALSE; + unset($this->mpd_version); + unset($this->errStr); + unset($this->mpd_sock); + } + + /* GetArtists() + * + * Returns the list of artists in the database in an associative array. + */ + function GetArtists() { + if ( $this->debugging ) echo "mpd->GetArtists()\n"; + if ( is_null($resp = $this->SendCommand(MPD_CMD_TABLE, MPD_TBL_ARTIST))) return NULL; + $arArray = array(); + + $arLine = strtok($resp,"\n"); + $arName = ""; + $arCounter = -1; + while ( $arLine ) { + list ( $element, $value ) = explode(": ",$arLine); + if ( $element == "Artist" ) { + $arCounter++; + $arName = $value; + $arArray[$arCounter] = $arName; + } + + $arLine = strtok("\n"); + } + if ( $this->debugging ) echo "mpd->GetArtists()\n"; + return $arArray; + } + + /* GetAlbums() + * + * Returns the list of albums in the database in an associative array. Optional parameter + * is an artist Name which will list all albums by a particular artist. + */ + function GetAlbums( $ar = NULL) { + if ( $this->debugging ) echo "mpd->GetAlbums()\n"; + if ( is_null($resp = $this->SendCommand(MPD_CMD_TABLE, MPD_TBL_ALBUM, $ar ))) return NULL; + $alArray = array(); + + $alLine = strtok($resp,"\n"); + $alName = ""; + $alCounter = -1; + while ( $alLine ) { + list ( $element, $value ) = explode(": ",$alLine); + if ( $element == "Album" ) { + $alCounter++; + $alName = $value; + $alArray[$alCounter] = $alName; + } + + $alLine = strtok("\n"); + } + if ( $this->debugging ) echo "mpd->GetAlbums()\n"; + return $alArray; + } + + //*******************************************************************************// + //***************************** INTERNAL FUNCTIONS ******************************// + //*******************************************************************************// + + /* _computeVersionValue() + * + * Computes a compatibility value from a version string + * + */ + function _computeVersionValue($verStr) { + list ($ver_maj, $ver_min, $ver_rel ) = explode("\.",$verStr); + return ( 100 * $ver_maj ) + ( 10 * $ver_min ) + ( $ver_rel ); + } + + /* _checkCompatibility() + * + * Check MPD command compatibility against our internal table. If there is no version + * listed in the table, allow it by default. + */ + function _checkCompatibility($cmd) { + // Check minimum compatibility + $req_ver_low = $this->COMPATIBILITY_MIN_TBL[$cmd]; + $req_ver_hi = $this->COMPATIBILITY_MAX_TBL[$cmd]; + + $mpd_ver = $this->_computeVersionValue($this->mpd_version); + + if ( $req_ver_low ) { + $req_ver = $this->_computeVersionValue($req_ver_low); + + if ( $mpd_ver < $req_ver ) { + $this->errStr = "Command '$cmd' is not compatible with this version of MPD, version ".$req_ver_low." required"; + return FALSE; + } + } + + // Check maxmum compatibility -- this will check for deprecations + if ( $req_ver_hi ) { + $req_ver = $this->_computeVersionValue($req_ver_hi); + + if ( $mpd_ver > $req_ver ) { + $this->errStr = "Command '$cmd' has been deprecated in this version of MPD."; + return FALSE; + } + } + + return TRUE; + } + + /* _parseFileListResponse() + * + * Builds a multidimensional array with MPD response lists. + * + * NOTE: This function is used internally within the class. It should not be used. + */ + function _parseFileListResponse($resp) { + if ( is_null($resp) ) { + return NULL; + } else { + $plistArray = array(); + $plistLine = strtok($resp,"\n"); + $plistFile = ""; + $plCounter = -1; + while ( $plistLine ) { + list ( $element, $value ) = explode(": ",$plistLine); + if($element == "file" || $element=="directory") { + $plCounter++; + $plistArray[$plCounter]['name']=$value; + $plistArray[$plCounter]['type']=$element; + $plistArray[$plCounter]['title']=$value; + } + if($element == "Name") { + $plistArray[$plCounter]['title']=$value; + } + $plistLine = strtok("\n"); + } + } + return $plistArray; + } + + /* RefreshInfo() + * + * Updates all class properties with the values from the MPD server. + * + * NOTE: This function is automatically called upon Connect() as of v1.1. + */ + function RefreshInfo() { + // Get the Server Statistics + $statStr = $this->SendCommand(MPD_CMD_STATISTICS); + if ( !$statStr ) { + return NULL; + } else { + $stats = array(); + $statLine = strtok($statStr,"\n"); + while ( $statLine ) { + list ( $element, $value ) = explode(": ",$statLine); + $stats[$element] = $value; + $statLine = strtok("\n"); + } + } + + // Get the Server Status + $statusStr = $this->SendCommand(MPD_CMD_STATUS); + if ( ! $statusStr ) { + return NULL; + } else { + $status = array(); + $statusLine = strtok($statusStr,"\n"); + while ( $statusLine ) { + list ( $element, $value ) = explode(": ",$statusLine); + $status[$element] = $value; + $statusLine = strtok("\n"); + } + } + + // Get the Playlist + $plStr = $this->SendCommand(MPD_CMD_PLLIST); + $this->playlist = $this->_parseFileListResponse($plStr); + $this->playlist_count = count($this->playlist); + + // Set Misc Other Variables + $this->state = $status['state']; + if ( ($this->state == MPD_STATE_PLAYING) || ($this->state == MPD_STATE_PAUSED) ) { + $this->current_track_id = $status['song']; + list ($this->current_track_position, $this->current_track_length ) = explode(":",$status['time']); + } else { + $this->current_track_id = -1; + $this->current_track_position = -1; + $this->current_track_length = -1; + } + + $this->repeat = $status['repeat']; + $this->random = $status['random']; + + $this->db_last_refreshed = $stats['db_update']; + + $this->volume = $status['volume']; + $this->uptime = $stats['uptime']; + $this->playtime = $stats['playtime']; + $this->num_songs_played = $stats['songs_played']; + $this->num_artists = $stats['num_artists']; + $this->num_songs = $stats['num_songs']; + $this->num_albums = $stats['num_albums']; + return TRUE; + } + + /* ------------------ DEPRECATED METHODS -------------------*/ + /* GetStatistics() + * + * Retrieves the 'statistics' variables from the server and tosses them into an array. + * + * NOTE: This function really should not be used. Instead, use $this->[variable]. The function + * will most likely be deprecated in future releases. + */ + function GetStatistics() { + if ( $this->debugging ) echo "mpd->GetStatistics()\n"; + $stats = $this->SendCommand(MPD_CMD_STATISTICS); + if ( !$stats ) { + return NULL; + } else { + $statsArray = array(); + $statsLine = strtok($stats,"\n"); + while ( $statsLine ) { + list ( $element, $value ) = explode(": ",$statsLine); + $statsArray[$element] = $value; + $statsLine = strtok("\n"); + } + } + if ( $this->debugging ) echo "mpd->GetStatistics() / return: " . print_r($statsArray) ."\n"; + return $statsArray; + } + + /* GetStatus() + * + * Retrieves the 'status' variables from the server and tosses them into an array. + * + * NOTE: This function really should not be used. Instead, use $this->[variable]. The function + * will most likely be deprecated in future releases. + */ + function GetStatus() { + if ( $this->debugging ) echo "mpd->GetStatus()\n"; + $status = $this->SendCommand(MPD_CMD_STATUS); + if ( ! $status ) { + return NULL; + } else { + $statusArray = array(); + $statusLine = strtok($status,"\n"); + while ( $statusLine ) { + list ( $element, $value ) = explode(": ",$statusLine); + $statusArray[$element] = $value; + $statusLine = strtok("\n"); + } + } + if ( $this->debugging ) echo "mpd->GetStatus() / return: " . print_r($statusArray) ."\n"; + return $statusArray; + } + + /* GetVolume() + * + * Retrieves the mixer volume from the server. + * + * NOTE: This function really should not be used. Instead, use $this->volume. The function + * will most likely be deprecated in future releases. + */ + function GetVolume() { + if ( $this->debugging ) echo "mpd->GetVolume()\n"; + $volLine = $this->SendCommand(MPD_CMD_STATUS); + if ( ! $volLine ) { + return NULL; + } else { + list ($vol) = sscanf($volLine,"volume: %d"); + } + if ( $this->debugging ) echo "mpd->GetVolume() / return: $vol\n"; + return $vol; + } + + /* GetPlaylist() + * + * Retrieves the playlist from the server and tosses it into a multidimensional array. + * + * NOTE: This function really should not be used. Instead, use $this->playlist. The function + * will most likely be deprecated in future releases. + */ + function GetPlaylist() { + if ( $this->debugging ) echo "mpd->GetPlaylist()\n"; + $resp = $this->SendCommand(MPD_CMD_PLLIST); + $playlist = $this->_parseFileListResponse($resp); + if ( $this->debugging ) echo "mpd->GetPlaylist() / return ".print_r($playlist)."\n"; + return $playlist; + } + + /* ----------------- Command compatibility tables --------------------- */ + var $COMPATIBILITY_MIN_TBL = array( + ); + + var $COMPATIBILITY_MAX_TBL = array( + ); + +} // ---------------------------- end of class ------------------------------ +?> diff --git a/view/default/tmpl/default.php b/view/default/tmpl/default.php new file mode 100644 index 0000000..269b508 --- /dev/null +++ b/view/default/tmpl/default.php @@ -0,0 +1,29 @@ + + + + + MPD Player + + + + + +
+ + + +
+ +
+
+ +
+ +
+ + + + + diff --git a/view/default/tmpl/playlistmenu.php b/view/default/tmpl/playlistmenu.php new file mode 100644 index 0000000..bc0f8f0 --- /dev/null +++ b/view/default/tmpl/playlistmenu.php @@ -0,0 +1,9 @@ +
+ + + +
+ + +
+
diff --git a/view/default/view.php b/view/default/view.php new file mode 100644 index 0000000..12276a0 --- /dev/null +++ b/view/default/view.php @@ -0,0 +1,4 @@ + diff --git a/view/player/tmpl/default.php b/view/player/tmpl/default.php new file mode 100644 index 0000000..e3c5315 --- /dev/null +++ b/view/player/tmpl/default.php @@ -0,0 +1,38 @@ +
+
+ + + + +
Now Playing
+
+
+
+
+ + + + + + + + + +
+
+ +
+
+ +
+ +
+ + + + + + +
Volume:
+
+
diff --git a/view/player/view.php b/view/player/view.php new file mode 100644 index 0000000..6cf1b29 --- /dev/null +++ b/view/player/view.php @@ -0,0 +1,6 @@ +