various fixups

This commit is contained in:
Andrew Karpow 2013-11-05 00:17:28 +01:00
parent c692feb111
commit d194c8439a
9 changed files with 734 additions and 90 deletions

138
htdocs/css/slider.css Normal file
View File

@ -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;
}

View File

@ -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
htdocs/js/bootstrap-slider.js vendored Normal file
View File

@ -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 );

6
htdocs/js/jquery-1.10.2.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -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");
}

View File

@ -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"},

View File

@ -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);

View File

@ -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(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);
}
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;
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 *p = &((*buffer)[LWS_SEND_BUFFER_PRE_PADDING]);
char *cur = buffer;
const char *end = buffer + MAX_SIZE;
struct mpd_entity *entity;
struct mpd_song const *song;
mpd_send_list_queue_meta(conn);
cur += snprintf(cur, end - cur, "{\"type\": \"playlist\", \"data\": [ ");
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;
}

View File

@ -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);