Browse Source

various fixups

Andrew Karpow 10 years ago
parent
commit
d194c8439a
9 changed files with 731 additions and 90 deletions
  1. 138 0
      htdocs/css/slider.css
  2. 16 31
      htdocs/index.html
  3. 388 0
      htdocs/js/bootstrap-slider.js
  4. 3 0
      htdocs/js/jquery-1.10.2.min.js
  5. 58 6
      htdocs/js/mpd.js
  6. 5 0
      src/http_server.c
  7. 2 1
      src/main.c
  8. 115 51
      src/mpd_client.c
  9. 6 1
      src/mpd_client.h

+ 138 - 0
htdocs/css/slider.css

@@ -0,0 +1,138 @@
+/*!
+ * Slider for Bootstrap
+ *
+ * Copyright 2012 Stefan Petre
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ */
+.slider {
+  display: inline-block;
+  vertical-align: middle;
+  position: relative;
+}
+.slider.slider-horizontal {
+  width: 210px;
+  height: 20px;
+}
+.slider.slider-horizontal .slider-track {
+  height: 10px;
+  width: 100%;
+  margin-top: -5px;
+  top: 50%;
+  left: 0;
+}
+.slider.slider-horizontal .slider-selection {
+  height: 100%;
+  top: 0;
+  bottom: 0;
+}
+.slider.slider-horizontal .slider-handle {
+  margin-left: -10px;
+  margin-top: -5px;
+}
+.slider.slider-horizontal .slider-handle.triangle {
+  border-width: 0 10px 10px 10px;
+  width: 0;
+  height: 0;
+  border-bottom-color: #0480be;
+  margin-top: 0;
+}
+.slider.slider-vertical {
+  height: 210px;
+  width: 20px;
+}
+.slider.slider-vertical .slider-track {
+  width: 10px;
+  height: 100%;
+  margin-left: -5px;
+  left: 50%;
+  top: 0;
+}
+.slider.slider-vertical .slider-selection {
+  width: 100%;
+  left: 0;
+  top: 0;
+  bottom: 0;
+}
+.slider.slider-vertical .slider-handle {
+  margin-left: -5px;
+  margin-top: -10px;
+}
+.slider.slider-vertical .slider-handle.triangle {
+  border-width: 10px 0 10px 10px;
+  width: 1px;
+  height: 1px;
+  border-left-color: #0480be;
+  margin-left: 0;
+}
+.slider input {
+  display: none;
+}
+.slider .tooltip-inner {
+  white-space: nowrap;
+}
+.slider-track {
+  position: absolute;
+  cursor: pointer;
+  background-color: #f7f7f7;
+  background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9));
+  background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9);
+  background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9);
+  background-image: linear-gradient(to bottom, #f5f5f5, #f9f9f9);
+  background-repeat: repeat-x;
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0);
+  -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+  -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+  -webkit-border-radius: 4px;
+  -moz-border-radius: 4px;
+  border-radius: 4px;
+}
+.slider-selection {
+  position: absolute;
+  background-color: #f7f7f7;
+  background-image: -moz-linear-gradient(top, #f9f9f9, #f5f5f5);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f9f9f9), to(#f5f5f5));
+  background-image: -webkit-linear-gradient(top, #f9f9f9, #f5f5f5);
+  background-image: -o-linear-gradient(top, #f9f9f9, #f5f5f5);
+  background-image: linear-gradient(to bottom, #f9f9f9, #f5f5f5);
+  background-repeat: repeat-x;
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9f9f9', endColorstr='#fff5f5f5', GradientType=0);
+  -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+  -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+  box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+  -webkit-box-sizing: border-box;
+  -moz-box-sizing: border-box;
+  box-sizing: border-box;
+  -webkit-border-radius: 4px;
+  -moz-border-radius: 4px;
+  border-radius: 4px;
+}
+.slider-handle {
+  position: absolute;
+  width: 20px;
+  height: 20px;
+  background-color: #0e90d2;
+  background-image: -moz-linear-gradient(top, #149bdf, #0480be);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be));
+  background-image: -webkit-linear-gradient(top, #149bdf, #0480be);
+  background-image: -o-linear-gradient(top, #149bdf, #0480be);
+  background-image: linear-gradient(to bottom, #149bdf, #0480be);
+  background-repeat: repeat-x;
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0);
+  -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
+  -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
+  box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
+  opacity: 0.8;
+  border: 0px solid transparent;
+}
+.slider-handle.round {
+  -webkit-border-radius: 20px;
+  -moz-border-radius: 20px;
+  border-radius: 20px;
+}
+.slider-handle.triangle {
+  background: transparent none;
+}

+ 16 - 31
htdocs/index.html

@@ -14,6 +14,7 @@
 
   <!-- Custom styles for this template -->
   <link href="css/starter-template.css" rel="stylesheet">
+  <link href="css/slider.css" rel="stylesheet">
 
   <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
   <!--[if lt IE 9]>
@@ -46,25 +47,26 @@
               <input type="text" class="form-control" placeholder="Search">
             </div>
             <button type="submit" class="btn btn-default">
-                <span class="glyphicon glyphicon-search"></span>
+            <span class="glyphicon glyphicon-search"></span>
             </button>
-          </form>   
+          </form>
 
           <div class="btn-toolbar navbar-btn navbar-right" role="toolbar">
             <div class="btn-group">
               <button type="button" class="btn btn-default">
-                <span class="glyphicon glyphicon-backward"></span>
+                <span class="glyphicon glyphicon-backward" onclick="socket.send('MPD_API_SET_NEXT')"></span>
               </button>
-              <button type="button" class="btn btn-default" data-toggle="button">
-                <span class="glyphicon glyphicon-pause"></span>
+              <button type="button" class="btn btn-default" onclick="socket.send('MPD_API_SET_PAUSE')">
+                <span id="play-icon" class="glyphicon glyphicon-pause"></span>
               </button>
-              <button type="button" class="btn btn-default" onclick="test();">
+              <button type="button" class="btn btn-default" onclick="socket.send('MPD_API_SET_PREV')">
                 <span class="glyphicon glyphicon-forward"></span>
               </button>
             </div>
             <div class="btn-group">
-              <button id="volume" type="button" class="btn btn-default" data-toggle="button">
-                <span id="volume-icon" class="glyphicon glyphicon-volume-off"></span>
+              <button type="button" class="btn btn-toolbar btn-default">
+                <span id="volume-icon" class="glyphicon glyphicon-volume-up"></span>
+                <input type="text" class="span2" value="0" data-slider-min="0" data-slider-max="100" data-slider-step="5" id="volumeslider" data-slider-tooltip="hide">
               </button>
             </div>
           </div>
@@ -74,7 +76,7 @@
     </div>
 
     <div class="progress progress-striped active">
-      <div class="progress-bar"  role="progressbar" aria-valuenow="45" aria-valuemin="0" aria-valuemax="100" style="width: 45%">
+      <div id="progressbar" class="progress-bar"  role="progressbar" aria-valuenow="45" aria-valuemin="0" aria-valuemax="100" style="width: 45%">
         <span class="sr-only">60% Complete</span>
       </div>
     </div>
@@ -86,34 +88,16 @@
           <div class="panel-heading">Playlist</div>
 
           <!-- Table -->
-          <table class="table">
+          <table id="salamisandwich" class="table">
             <thead>
               <tr>
                 <th>#</th>
                 <th>Track</th>
-                <th>Last Name</th>
-                <th>Username</th>
+                <th>Title</th>
+                <th>Duration</th>
               </tr>
             </thead>
             <tbody>
-              <tr>
-                <td>1</td>
-                <td>Mark</td>
-                <td>Otto</td>
-                <td>@mdo</td>
-              </tr>
-              <tr>
-                <td>2</td>
-                <td>Jacob</td>
-                <td>Thornton</td>
-                <td>@fat</td>
-              </tr>
-              <tr>
-                <td>3</td>
-                <td>Larry</td>
-                <td>the Bird</td>
-                <td>@twitter</td>
-              </tr>
             </tbody>
           </table>
         </div>
@@ -125,8 +109,9 @@
     <!-- Bootstrap core JavaScript
     ================================================== -->
     <!-- Placed at the end of the document so the pages load faster -->
-    <script src="https://code.jquery.com/jquery-1.10.2.min.js"></script>
+    <script src="js/jquery-1.10.2.min.js"></script>
     <script src="js/bootstrap.min.js"></script>
+    <script src="js/bootstrap-slider.js"></script>
     <script src="js/mpd.js"></script>
   </body>
   </html>

+ 388 - 0
htdocs/js/bootstrap-slider.js

@@ -0,0 +1,388 @@
+/* =========================================================
+ * bootstrap-slider.js v2.0.0
+ * http://www.eyecon.ro/bootstrap-slider
+ * =========================================================
+ * Copyright 2012 Stefan Petre
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================= */
+ 
+!function( $ ) {
+
+	var Slider = function(element, options) {
+		this.element = $(element);
+		this.picker = $('<div class="slider">'+
+							'<div class="slider-track">'+
+								'<div class="slider-selection"></div>'+
+								'<div class="slider-handle"></div>'+
+								'<div class="slider-handle"></div>'+
+							'</div>'+
+							'<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'+
+						'</div>')
+							.insertBefore(this.element)
+							.append(this.element);
+		this.id = this.element.data('slider-id')||options.id;
+		if (this.id) {
+			this.picker[0].id = this.id;
+		}
+
+		if (typeof Modernizr !== 'undefined' && Modernizr.touch) {
+			this.touchCapable = true;
+		}
+
+		var tooltip = this.element.data('slider-tooltip')||options.tooltip;
+
+		this.tooltip = this.picker.find('.tooltip');
+		this.tooltipInner = this.tooltip.find('div.tooltip-inner');
+
+		this.orientation = this.element.data('slider-orientation')||options.orientation;
+		switch(this.orientation) {
+			case 'vertical':
+				this.picker.addClass('slider-vertical');
+				this.stylePos = 'top';
+				this.mousePos = 'pageY';
+				this.sizePos = 'offsetHeight';
+				this.tooltip.addClass('right')[0].style.left = '100%';
+				break;
+			default:
+				this.picker
+					.addClass('slider-horizontal')
+					.css('width', this.element.outerWidth());
+				this.orientation = 'horizontal';
+				this.stylePos = 'left';
+				this.mousePos = 'pageX';
+				this.sizePos = 'offsetWidth';
+				this.tooltip.addClass('top')[0].style.top = -this.tooltip.outerHeight() - 14 + 'px';
+				break;
+		}
+
+		this.min = this.element.data('slider-min')||options.min;
+		this.max = this.element.data('slider-max')||options.max;
+		this.step = this.element.data('slider-step')||options.step;
+		this.value = this.element.data('slider-value')||options.value;
+		if (this.value[1]) {
+			this.range = true;
+		}
+
+		this.selection = this.element.data('slider-selection')||options.selection;
+		this.selectionEl = this.picker.find('.slider-selection');
+		if (this.selection === 'none') {
+			this.selectionEl.addClass('hide');
+		}
+		this.selectionElStyle = this.selectionEl[0].style;
+
+
+		this.handle1 = this.picker.find('.slider-handle:first');
+		this.handle1Stype = this.handle1[0].style;
+		this.handle2 = this.picker.find('.slider-handle:last');
+		this.handle2Stype = this.handle2[0].style;
+
+		var handle = this.element.data('slider-handle')||options.handle;
+		switch(handle) {
+			case 'round':
+				this.handle1.addClass('round');
+				this.handle2.addClass('round');
+				break
+			case 'triangle':
+				this.handle1.addClass('triangle');
+				this.handle2.addClass('triangle');
+				break
+		}
+
+		if (this.range) {
+			this.value[0] = Math.max(this.min, Math.min(this.max, this.value[0]));
+			this.value[1] = Math.max(this.min, Math.min(this.max, this.value[1]));
+		} else {
+			this.value = [ Math.max(this.min, Math.min(this.max, this.value))];
+			this.handle2.addClass('hide');
+			if (this.selection == 'after') {
+				this.value[1] = this.max;
+			} else {
+				this.value[1] = this.min;
+			}
+		}
+		this.diff = this.max - this.min;
+		this.percentage = [
+			(this.value[0]-this.min)*100/this.diff,
+			(this.value[1]-this.min)*100/this.diff,
+			this.step*100/this.diff
+		];
+
+		this.offset = this.picker.offset();
+		this.size = this.picker[0][this.sizePos];
+
+		this.formater = options.formater;
+
+		this.layout();
+
+		if (this.touchCapable) {
+			// Touch: Bind touch events:
+			this.picker.on({
+				touchstart: $.proxy(this.mousedown, this)
+			});
+		} else {
+			this.picker.on({
+				mousedown: $.proxy(this.mousedown, this)
+			});
+		}
+
+		if (tooltip === 'show') {
+			this.picker.on({
+				mouseenter: $.proxy(this.showTooltip, this),
+				mouseleave: $.proxy(this.hideTooltip, this)
+			});
+		} else {
+			this.tooltip.addClass('hide');
+		}
+	};
+
+	Slider.prototype = {
+		constructor: Slider,
+
+		over: false,
+		inDrag: false,
+		
+		showTooltip: function(){
+			this.tooltip.addClass('in');
+			//var left = Math.round(this.percent*this.width);
+			//this.tooltip.css('left', left - this.tooltip.outerWidth()/2);
+			this.over = true;
+		},
+		
+		hideTooltip: function(){
+			if (this.inDrag === false) {
+				this.tooltip.removeClass('in');
+			}
+			this.over = false;
+		},
+
+		layout: function(){
+			this.handle1Stype[this.stylePos] = this.percentage[0]+'%';
+			this.handle2Stype[this.stylePos] = this.percentage[1]+'%';
+			if (this.orientation == 'vertical') {
+				this.selectionElStyle.top = Math.min(this.percentage[0], this.percentage[1]) +'%';
+				this.selectionElStyle.height = Math.abs(this.percentage[0] - this.percentage[1]) +'%';
+			} else {
+				this.selectionElStyle.left = Math.min(this.percentage[0], this.percentage[1]) +'%';
+				this.selectionElStyle.width = Math.abs(this.percentage[0] - this.percentage[1]) +'%';
+			}
+			if (this.range) {
+				this.tooltipInner.text(
+					this.formater(this.value[0]) + 
+					' : ' + 
+					this.formater(this.value[1])
+				);
+				this.tooltip[0].style[this.stylePos] = this.size * (this.percentage[0] + (this.percentage[1] - this.percentage[0])/2)/100 - (this.orientation === 'vertical' ? this.tooltip.outerHeight()/2 : this.tooltip.outerWidth()/2) +'px';
+			} else {
+				this.tooltipInner.text(
+					this.formater(this.value[0])
+				);
+				this.tooltip[0].style[this.stylePos] = this.size * this.percentage[0]/100 - (this.orientation === 'vertical' ? this.tooltip.outerHeight()/2 : this.tooltip.outerWidth()/2) +'px';
+			}
+		},
+
+		mousedown: function(ev) {
+
+			// Touch: Get the original event:
+			if (this.touchCapable && ev.type === 'touchstart') {
+				ev = ev.originalEvent;
+			}
+
+			this.offset = this.picker.offset();
+			this.size = this.picker[0][this.sizePos];
+
+			var percentage = this.getPercentage(ev);
+
+			if (this.range) {
+				var diff1 = Math.abs(this.percentage[0] - percentage);
+				var diff2 = Math.abs(this.percentage[1] - percentage);
+				this.dragged = (diff1 < diff2) ? 0 : 1;
+			} else {
+				this.dragged = 0;
+			}
+
+			this.percentage[this.dragged] = percentage;
+			this.layout();
+
+			if (this.touchCapable) {
+				// Touch: Bind touch events:
+				$(document).on({
+					touchmove: $.proxy(this.mousemove, this),
+					touchend: $.proxy(this.mouseup, this)
+				});
+			} else {
+				$(document).on({
+					mousemove: $.proxy(this.mousemove, this),
+					mouseup: $.proxy(this.mouseup, this)
+				});
+			}
+
+			this.inDrag = true;
+			var val = this.calculateValue();
+			this.element.trigger({
+					type: 'slideStart',
+					value: val
+				}).trigger({
+					type: 'slide',
+					value: val
+				});
+			return false;
+		},
+
+		mousemove: function(ev) {
+			
+			// Touch: Get the original event:
+			if (this.touchCapable && ev.type === 'touchmove') {
+				ev = ev.originalEvent;
+			}
+
+			var percentage = this.getPercentage(ev);
+			if (this.range) {
+				if (this.dragged === 0 && this.percentage[1] < percentage) {
+					this.percentage[0] = this.percentage[1];
+					this.dragged = 1;
+				} else if (this.dragged === 1 && this.percentage[0] > percentage) {
+					this.percentage[1] = this.percentage[0];
+					this.dragged = 0;
+				}
+			}
+			this.percentage[this.dragged] = percentage;
+			this.layout();
+			var val = this.calculateValue();
+			this.element
+				.trigger({
+					type: 'slide',
+					value: val
+				})
+				.data('value', val)
+				.prop('value', val);
+			return false;
+		},
+
+		mouseup: function(ev) {
+			if (this.touchCapable) {
+				// Touch: Bind touch events:
+				$(document).off({
+					touchmove: this.mousemove,
+					touchend: this.mouseup
+				});
+			} else {
+				$(document).off({
+					mousemove: this.mousemove,
+					mouseup: this.mouseup
+				});
+			}
+
+			this.inDrag = false;
+			if (this.over == false) {
+				this.hideTooltip();
+			}
+			this.element;
+			var val = this.calculateValue();
+			this.element
+				.trigger({
+					type: 'slideStop',
+					value: val
+				})
+				.data('value', val)
+				.prop('value', val);
+			return false;
+		},
+
+		calculateValue: function() {
+			var val;
+			if (this.range) {
+				val = [
+					(this.min + Math.round((this.diff * this.percentage[0]/100)/this.step)*this.step),
+					(this.min + Math.round((this.diff * this.percentage[1]/100)/this.step)*this.step)
+				];
+				this.value = val;
+			} else {
+				val = (this.min + Math.round((this.diff * this.percentage[0]/100)/this.step)*this.step);
+				this.value = [val, this.value[1]];
+			}
+			return val;
+		},
+
+		getPercentage: function(ev) {
+			if (this.touchCapable) {
+				ev = ev.touches[0];
+			}
+			var percentage = (ev[this.mousePos] - this.offset[this.stylePos])*100/this.size;
+			percentage = Math.round(percentage/this.percentage[2])*this.percentage[2];
+			return Math.max(0, Math.min(100, percentage));
+		},
+
+		getValue: function() {
+			if (this.range) {
+				return this.value;
+			}
+			return this.value[0];
+		},
+
+		setValue: function(val) {
+			this.value = val;
+
+			if (this.range) {
+				this.value[0] = Math.max(this.min, Math.min(this.max, this.value[0]));
+				this.value[1] = Math.max(this.min, Math.min(this.max, this.value[1]));
+			} else {
+				this.value = [ Math.max(this.min, Math.min(this.max, this.value))];
+				this.handle2.addClass('hide');
+				if (this.selection == 'after') {
+					this.value[1] = this.max;
+				} else {
+					this.value[1] = this.min;
+				}
+			}
+			this.diff = this.max - this.min;
+			this.percentage = [
+				(this.value[0]-this.min)*100/this.diff,
+				(this.value[1]-this.min)*100/this.diff,
+				this.step*100/this.diff
+			];
+			this.layout();
+		}
+	};
+
+	$.fn.slider = function ( option, val ) {
+		return this.each(function () {
+			var $this = $(this),
+				data = $this.data('slider'),
+				options = typeof option === 'object' && option;
+			if (!data)  {
+				$this.data('slider', (data = new Slider(this, $.extend({}, $.fn.slider.defaults,options))));
+			}
+			if (typeof option == 'string') {
+				data[option](val);
+			}
+		})
+	};
+
+	$.fn.slider.defaults = {
+		min: 0,
+		max: 10,
+		step: 1,
+		orientation: 'horizontal',
+		value: 5,
+		selection: 'before',
+		tooltip: 'show',
+		handle: 'round',
+		formater: function(value) {
+			return value;
+		}
+	};
+
+	$.fn.slider.Constructor = Slider;
+
+}( window.jQuery );

File diff suppressed because it is too large
+ 3 - 0
htdocs/js/jquery-1.10.2.min.js


+ 58 - 6
htdocs/js/mpd.js

@@ -6,16 +6,42 @@ if (typeof MozWebSocket != "undefined") {
 	socket = new WebSocket(get_appropriate_ws_url(), "ympd-client");
 }
 
-
 try {
 	socket.onopen = function() {
 		console.log("Connected");
+		init();
 	} 
 
 	socket.onmessage =function got_packet(msg) {
 		console.log(msg.data);
-	} 
+		var obj = JSON.parse(msg.data);
+		switch (obj.type) {
+		  case "playlist":
+		  	for (var song in obj.data) {
+		  		var minutes = Math.floor(obj.data[song].duration / 60);
+		  		var seconds = obj.data[song].duration - minutes * 60;
 
+		  		$('#salamisandwich tr:last').after(
+		  			"<tr id=\"playlist_" + obj.data[song].id + "\"><td>" + obj.data[song].id + "</td>" +
+		  			"<td>"+ obj.data[song].uri +"</td>" +
+		  			"<td>"+ obj.data[song].title.replace(/%07/g, '"') +"</td>" + 
+		  			"<td>"+ minutes + ":" + (seconds < 10 ? '0' : '') + seconds +"</td></tr>");
+		  	}
+		    break;
+		  case "state":
+		  	$('#volumeslider').slider('setValue', obj.data.volume)
+		  	var progress = Math.floor(100*obj.data.elapsedTime/obj.data.totalTime) + "%";
+		  	$('#progressbar').width(progress);
+		  	$('#playlist_'+obj.data.currentsongid).addClass('active');
+		  	updatePlayIcon(obj.data.state);
+		  	updateVolumeIcon(obj.data.volume);
+
+		  	break;
+		  default:
+		    alert("Sie bleiben leider dumm");
+		    break;
+		}
+	}
 	socket.onclose = function(){
 		console.log("Disconnected");
 
@@ -24,6 +50,10 @@ try {
 	alert('<p>Error' + exception);  
 }
 
+$('#volumeslider').slider().on('slide', function(event) {
+    socket.send("MPD_API_SET_VOLUME,"+event.value);
+});
+
 function get_appropriate_ws_url()
 {
 	var pcol;
@@ -48,20 +78,42 @@ function get_appropriate_ws_url()
 	return pcol + u[0];
 }
 
-var updateVolumeIcon = function(mute)
+var updateVolumeIcon = function(volume)
 {
 	$("#volume-icon").removeClass("glyphicon-volume-off");
 	$("#volume-icon").removeClass("glyphicon-volume-up");
+	$("#volume-icon").removeClass("glyphicon-volume-down");
 
-	if(mute) {
+	if(volume == 0) {
 		$("#volume-icon").addClass("glyphicon-volume-off");
-		$("#volume").button('toggle')
+	} else if (volume < 50) {
+		$("#volume-icon").addClass("glyphicon-volume-down");
 	} else {
 		$("#volume-icon").addClass("glyphicon-volume-up");
-		$("#volume").button('reset')
 	}
 }
 
+var updatePlayIcon = function(state)
+{
+	$("#play-icon").removeClass("glyphicon-play");
+	$("#play-icon").removeClass("glyphicon-pause");
+	$("#play-icon").removeClass("glyphicon-stop");
+
+	if(state == 1) {
+		$("#play-icon").addClass("glyphicon-stop");
+	} else if(state == 2) {
+		$("#play-icon").addClass("glyphicon-pause");
+	} else {
+		$("#play-icon").addClass("glyphicon-play");
+	}
+}
+
+
+function init() {
+	socket.send("MPD_API_GET_PLAYLIST");
+	socket.send("MPD_API_GET_STATE");
+}
+
 function test() {
 	socket.send("MPD_API_GET_PLAYLIST");
 }

+ 5 - 0
src/http_server.c

@@ -14,8 +14,13 @@ struct serveable {
 static const struct serveable whitelist[] = {
 	{ "/css/bootstrap.css", "text/css" },
 	{ "/css/starter-template.css", "text/css" },
+	{ "/css/slider.css", "text/css" },
+
 	{ "/js/bootstrap.min.js", "text/javascript" },
 	{ "/js/mpd.js", "text/javascript" },
+	{ "/js/jquery-1.10.2.min.js", "text/javascript" },
+	{ "/js/bootstrap-slider.js", "text/javascript" },
+	
 	{ "/fonts/glyphicons-halflings-regular.woff", "application/x-font-woff"},
 	{ "/fonts/glyphicons-halflings-regular.svg", "image/svg+xml"},
 	{ "/fonts/glyphicons-halflings-regular.ttf", "application/x-font-ttf"},

+ 2 - 1
src/main.c

@@ -6,7 +6,7 @@
 #include "http_server.h"
 #include "mpd_client.h"
 
-static struct libwebsocket_protocols protocols[] = {
+struct libwebsocket_protocols protocols[] = {
 	/* first protocol must always be HTTP handler */
 	{
 		"http-only",		/* name */
@@ -40,6 +40,7 @@ int main(int argc, char **argv)
 	const char *iface = NULL;
 	struct lws_context_creation_info info;
 	unsigned int oldus = 0;
+	protocol_array = protocols;
 
 	atexit(bye);
 	memset(&info, 0, sizeof info);

+ 115 - 51
src/mpd_client.c

@@ -13,9 +13,12 @@
 
 #include "mpd_client.h"
 
+#define MAX_SIZE 9000*10
+
 struct mpd_connection *conn = NULL;
 enum mpd_conn_states mpd_conn_state = MPD_DISCONNECTED;
 enum mpd_state mpd_play_state = MPD_STATE_UNKNOWN;
+static int global_send;
 
 callback_ympd(struct libwebsocket_context *context,
 	struct libwebsocket *wsi,
@@ -23,47 +26,84 @@ callback_ympd(struct libwebsocket_context *context,
 	void *user, void *in, size_t len)
 {
 	int n, m;
-	/*unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 512 +
-		LWS_SEND_BUFFER_POST_PADDING];*/
-	char *buf;
+	char *buf = NULL, *p;
 	struct per_session_data__ympd *pss = (struct per_session_data__ympd *)user;
 
-	//memset(buf, 0, sizeof(buf));
+	//if(global_send || (pss != NULL && pss->do_send))
+	//{
+		buf = (char *)malloc(MAX_SIZE + LWS_SEND_BUFFER_PRE_PADDING + LWS_SEND_BUFFER_POST_PADDING);
+
+		if(buf == NULL)
+			return -1;
+
+		p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
+	//}
 
 	switch (reason) {
 		case LWS_CALLBACK_ESTABLISHED:
-		lwsl_info("callback_dumb_increment: "
-			"LWS_CALLBACK_ESTABLISHED\n");
-		break;
+			lwsl_info("callback_dumb_increment: "
+				"LWS_CALLBACK_ESTABLISHED\n");
+			break;
 
 		case LWS_CALLBACK_SERVER_WRITEABLE:
-
-		if(pss->do_send & DO_SEND_STATE)
-			n = mpd_put_state(buf);
-		else if(pss->do_send & DO_SEND_PLAYLIST)
-		{
-			printf("Before buf is %ud\n",buf);
-			n = mpd_put_playlist(&buf);
-			printf("n is %d, buf is %ud\n", n, buf);
-			pss->do_send &= ~DO_SEND_PLAYLIST;
-		}
-		else if(pss->do_send & DO_SEND_TRACK_INFO)
-			n = mpd_put_current_song(buf);
-		else
-			return 0;
-
-		m = libwebsocket_write(wsi, buf, n, LWS_WRITE_TEXT);
-		if (m < n) {
-			lwsl_err("ERROR %d writing to socket\n", n);
-			return -1;
-		}
-		break;
+			if(pss->do_send & DO_SEND_STATE)
+			{
+				n = mpd_put_state(p);
+				pss->do_send &= ~DO_SEND_STATE;
+			}
+			else if(pss->do_send & DO_SEND_PLAYLIST)
+			{
+				n = mpd_put_playlist(p);
+				pss->do_send &= ~DO_SEND_PLAYLIST;
+			}
+			else if(pss->do_send & DO_SEND_TRACK_INFO)
+				n = mpd_put_current_song(p);
+			else
+			{
+				n = mpd_put_state(p);
+			}
+
+			m = libwebsocket_write(wsi, p, n, LWS_WRITE_TEXT);
+			if (m < n) {
+				lwsl_err("ERROR %d writing to socket\n", n);
+				return -1;
+			}
+			break;
 
 		case LWS_CALLBACK_RECEIVE:
-		if(!strcmp((const char *)in, MPD_API_GET_STATE))
-			pss->do_send |= DO_SEND_STATE;
-		if(!strcmp((const char *)in, MPD_API_GET_PLAYLIST))
-			pss->do_send |= DO_SEND_PLAYLIST;
+			printf("Got %s\n", (char *)in);
+
+			if(!strcmp((const char *)in, MPD_API_GET_STATE))
+				pss->do_send |= DO_SEND_STATE;
+			else if(!strcmp((const char *)in, MPD_API_GET_PLAYLIST))
+				pss->do_send |= DO_SEND_PLAYLIST;
+			else if(!strcmp((const char *)in, MPD_API_SET_PAUSE))
+			{
+				mpd_send_toggle_pause(conn);
+				mpd_response_finish(conn);
+			}
+			else if(!strcmp((const char *)in, MPD_API_SET_PREV))
+			{
+				mpd_send_previous(conn);
+				mpd_response_finish(conn);
+			}
+			else if(!strcmp((const char *)in, MPD_API_SET_NEXT))
+			{
+				mpd_send_next(conn);
+				mpd_response_finish(conn);
+			}
+			else if(!strncmp((const char *)in, MPD_API_SET_VOLUME, sizeof(MPD_API_SET_VOLUME)-1))
+			{
+				unsigned int volume;
+				if(sscanf(in, "MPD_API_SET_VOLUME,%ud", &volume) && volume < 100)
+				{
+					printf("Setting volume to %d\n", volume);
+					mpd_run_set_volume(conn, volume);
+
+				}
+			}
+
+
 
 		break;
 	/*
@@ -89,7 +129,7 @@ void mpd_loop()
 		case MPD_DISCONNECTED:
 			/* Try to connect */
 
-		conn = mpd_connection_new("127.0.0.1", 6600, 3000);
+		conn = mpd_connection_new("10.23.44.2", 6600, 3000);
 		if (conn == NULL) {
 			lwsl_err("%s", "Out of memory");
 			mpd_conn_state = MPD_FAILURE;
@@ -119,9 +159,19 @@ void mpd_loop()
 
 }
 
+const char* encode_string(const char *str)
+{
+	char *ptr = (char *)str;
+	while(*ptr++ != '\0')
+		if(*ptr=='"')
+			*ptr=' ';
+	return str;
+}
+
 int mpd_put_state(char* buffer)
 {
 	struct mpd_status *status;
+	int len;
 
 	status = mpd_run_status(conn);
 	if (!status) {
@@ -130,11 +180,12 @@ int mpd_put_state(char* buffer)
 		return;
 	}
 
-	sprintf(buffer, 
-		"{\"type\": \"state\", \"data\": {"
+	len = snprintf(buffer, MAX_SIZE,
+		"{\"type\":\"state\", \"data\":{"
 		" \"state\":%d, \"volume\":%d, \"repeat\":%d,"
         " \"single\":%d, \"consume\":%d, \"random\":%d, "
-        " \"songpos\": %d, \"elapsedTime\": %d, \"totalTime\":%d"
+        " \"songpos\": %d, \"elapsedTime\": %d, \"totalTime\":%d, "
+        " \"currentsongid\": %d"
 	    "}}", 
 		mpd_status_get_state(status),
 		mpd_status_get_volume(status), 
@@ -144,10 +195,13 @@ int mpd_put_state(char* buffer)
 		mpd_status_get_random(status),
 		mpd_status_get_song_pos(status),
 		mpd_status_get_elapsed_time(status),
-		mpd_status_get_total_time(status));
+		mpd_status_get_total_time(status),
+		mpd_status_get_song_id(status));
 
+	printf("buffer: %s\n", buffer);
 	mpd_status_free(status);
-	mpd_response_finish(conn);
+	//printf("status: %d\n", mpd_response_finish(conn));
+	return len;
 }
 
 int mpd_put_current_song(char* buffer)
@@ -167,27 +221,37 @@ int mpd_put_current_song(char* buffer)
 	mpd_response_finish(conn);
 }
 
-int mpd_put_playlist(char** buffer)
+int mpd_put_playlist(char* buffer)
 {
-	mpd_send_list_queue_range_meta(conn, 0, 10);
-	int max_size = 1024*200;
-	*buffer = (char **)malloc(max_size + LWS_SEND_BUFFER_PRE_PADDING + LWS_SEND_BUFFER_POST_PADDING);
-	printf("Buffer init: %p %ud\n", *buffer, *buffer);
+	char *cur = buffer;
+	const char *end = buffer + MAX_SIZE;
+	struct mpd_entity *entity;
+	struct mpd_song const *song;
 
-	char *p = &((*buffer)[LWS_SEND_BUFFER_PRE_PADDING]);
+	mpd_send_list_queue_meta(conn);
+
+	cur += snprintf(cur, end  - cur, "{\"type\": \"playlist\", \"data\": [ ");
 
-	struct mpd_entity *entity;
 	for(entity = mpd_recv_entity(conn); entity; entity = mpd_recv_entity(conn)) {
 
 		if(mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG) {
-			struct mpd_song* song = mpd_entity_get_song(entity);
-			p += snprintf(p, max_size, "ID: %d, Song: %s\n", mpd_song_get_id(song), mpd_song_get_uri(song));
+			song = mpd_entity_get_song(entity);
+			cur += snprintf(cur, end  - cur, 
+				"{\"id\":%d, \"uri\":\"%s\", \"duration\":%d, \"title\":\"%s\"},",
+				mpd_song_get_id(song),
+				mpd_song_get_uri(song),
+				mpd_song_get_duration(song),
+				encode_string(mpd_song_get_tag(song, MPD_TAG_TITLE, 0))
+			);
 		}
 
-		if(entity != NULL)
-			mpd_entity_free(entity);
+		mpd_entity_free(entity);
 	}
+	//printf("status: %d\n", mpd_response_finish(conn));
 
-	printf("strlen is %d\n", strlen(p));
-	return strlen(*buffer + LWS_SEND_BUFFER_PRE_PADDING) +1;
+	/* remove last ',' */
+	cur--;
+	cur += snprintf(cur, end  - cur, "] }");
+	printf("buffer: %s\n", buffer);
+	return cur - buffer;
 }

+ 6 - 1
src/mpd_client.h

@@ -4,6 +4,8 @@
 #define DO_SEND_PLAYLIST   (1 << 1)
 #define DO_SEND_TRACK_INFO (1 << 2)
 
+struct libwebsocket_protocols *protocol_array;
+
 struct per_session_data__ympd {
 	int do_send;
 };
@@ -25,6 +27,9 @@ enum mpd_conn_states {
 #define MPD_API_SET_PLAY         "MPD_API_SET_PLAY"
 #define MPD_API_SET_STOP         "MPD_API_SET_STOP"
 #define MPD_API_SET_SEEK         "MPD_API_SET_SEEK"
+#define MPD_API_SET_NEXT         "MPD_API_SET_PREV"
+#define MPD_API_SET_PREV         "MPD_API_SET_NEXT"
+
 
 
 int callback_ympd(struct libwebsocket_context *context,
@@ -35,4 +40,4 @@ int callback_ympd(struct libwebsocket_context *context,
 void mpd_connect();
 int mpd_put_state(char* buffer);
 int mpd_put_current_song(char* buffer);
-int mpd_put_playlist(char** buffer);
+int mpd_put_playlist(char* buffer);

Some files were not shown because too many files changed in this diff