mirror of
https://github.com/SuperBFG7/ympd
synced 2025-01-15 03:35:48 +00:00
initial mongoose checkin
This commit is contained in:
parent
5920d9f1bf
commit
79e38e7edd
@ -5,27 +5,15 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/")
|
|||||||
set(CPACK_PACKAGE_VERSION_MAJOR "1")
|
set(CPACK_PACKAGE_VERSION_MAJOR "1")
|
||||||
set(CPACK_PACKAGE_VERSION_MINOR "0")
|
set(CPACK_PACKAGE_VERSION_MINOR "0")
|
||||||
set(CPACK_PACKAGE_VERSION_PATCH "0")
|
set(CPACK_PACKAGE_VERSION_PATCH "0")
|
||||||
set(CPACK_GENERATOR "DEB;RPM;TGZ")
|
|
||||||
set(CPACK_SOURCE_GENERATOR "TBZ2")
|
|
||||||
set(DEBIAN_PACKAGE_SECTION "web")
|
|
||||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "MPD web client based on Websockets and Bootstrap")
|
|
||||||
set(CPACK_PACKAGE_CONTACT "Andrew Karpow <andy@ympd.org>")
|
|
||||||
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "andy@ndyk.de")
|
|
||||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libssl1.0.0,libmpdclient2")
|
|
||||||
|
|
||||||
option(WITH_STATIC_WEBSOCKETS "Build with static libwebsockets library" ON)
|
|
||||||
option(WITH_MPD_HOST_CHANGE "Let users of the web frontend change the MPD Host" ON)
|
option(WITH_MPD_HOST_CHANGE "Let users of the web frontend change the MPD Host" ON)
|
||||||
|
|
||||||
find_package(LibMPDClient REQUIRED)
|
find_package(LibMPDClient REQUIRED)
|
||||||
find_package(LibWebSockets REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
if(WITH_STATIC_WEBSOCKETS)
|
|
||||||
find_package(OpenSSL REQUIRED)
|
|
||||||
find_package(ZLIB REQUIRED)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
configure_file(${PROJECT_SOURCE_DIR}/src/config.h.in
|
configure_file(${PROJECT_SOURCE_DIR}/src/config.h.in
|
||||||
${PROJECT_BINARY_DIR}/config.h)
|
${PROJECT_BINARY_DIR}/config.h)
|
||||||
include_directories(${PROJECT_BINARY_DIR} ${LIBWEBSOCKETS_INCLUDE_DIR})
|
include_directories(${PROJECT_BINARY_DIR})
|
||||||
|
|
||||||
include(CheckCSourceCompiles)
|
include(CheckCSourceCompiles)
|
||||||
include(CPack)
|
include(CPack)
|
||||||
@ -33,25 +21,20 @@ include(CPack)
|
|||||||
set(CMAKE_C_FLAGS "-std=gnu99 -Wall")
|
set(CMAKE_C_FLAGS "-std=gnu99 -Wall")
|
||||||
set(CMAKE_C_FLAGS_DEBUG "-ggdb -pedantic")
|
set(CMAKE_C_FLAGS_DEBUG "-ggdb -pedantic")
|
||||||
|
|
||||||
|
add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/http_files.h
|
||||||
|
COMMAND perl mkdata.pl index.html js/* assets/* css/* fonts/* > ${PROJECT_BINARY_DIR}/http_files.h
|
||||||
|
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/htdocs
|
||||||
|
)
|
||||||
|
|
||||||
set(SOURCES
|
set(SOURCES
|
||||||
src/ympd.c
|
src/ympd.c
|
||||||
src/http_server.c
|
src/http_server.c
|
||||||
src/mpd_client.c
|
src/mpd_client.c
|
||||||
|
src/mongoose.c
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(ympd ${SOURCES})
|
add_executable(ympd ${SOURCES} ${PROJECT_BINARY_DIR}/http_files.h)
|
||||||
|
target_link_libraries(ympd ${LIBMPDCLIENT_LIBRARY} ${CMAKE_THREAD_LIBS_INIT})
|
||||||
# TODO: use generator expressions introduced to CMake 2.8.12, too fresh yet
|
|
||||||
if(WITH_STATIC_WEBSOCKETS)
|
|
||||||
find_library(LIBWEBSOCKETS_LIBRARY_STATIC libwebsockets.a)
|
|
||||||
target_link_libraries(ympd ${LIBMPDCLIENT_LIBRARY}
|
|
||||||
${LIBWEBSOCKETS_LIBRARY_STATIC} ${OPENSSL_LIBRARIES} ${ZLIB_LIBRARIES})
|
|
||||||
else()
|
|
||||||
target_link_libraries(ympd ${LIBMPDCLIENT_LIBRARY}
|
|
||||||
${LIBWEBSOCKETS_LIBRARIES})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
|
|
||||||
install(TARGETS ympd DESTINATION bin)
|
install(TARGETS ympd DESTINATION bin)
|
||||||
install(FILES ympd.1 DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man/man1)
|
install(FILES ympd.1 DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man/man1)
|
||||||
install(DIRECTORY htdocs DESTINATION share/${PROJECT_NAME})
|
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
# This module tries to find libWebsockets library and include files
|
|
||||||
#
|
|
||||||
# LIBWEBSOCKETS_FOUND, If false, do not try to use libWebSockets
|
|
||||||
# LIBWEBSOCKETS_INCLUDE_DIR, path where to find libwebsockets.h
|
|
||||||
# LIBWEBSOCKETS_LIBRARY_DIR, path where to find libwebsockets.so
|
|
||||||
# LIBWEBSOCKETS_LIBRARIES, the library to link against
|
|
||||||
#
|
|
||||||
# This currently works probably only for Linux
|
|
||||||
|
|
||||||
find_package(PkgConfig)
|
|
||||||
pkg_check_modules(PC_LIBWEBSOCKETS QUIET libwebsockets)
|
|
||||||
set(LIBWEBSOCKETS_DEFINITIONS ${PC_LIBWEBSOCKETS_CFLAGS_OTHER})
|
|
||||||
|
|
||||||
find_path(LIBWEBSOCKETS_INCLUDE_DIR libwebsockets.h
|
|
||||||
HINTS ${PC_LIBWEBSOCKETS_INCLUDEDIR} ${PC_LIBWEBSOCKETS_INCLUDE_DIRS}
|
|
||||||
)
|
|
||||||
|
|
||||||
find_library(LIBWEBSOCKETS_LIBRARY websockets
|
|
||||||
HINTS ${PC_LIBWEBSOCKETS_LIBDIR} ${PC_LIBWEBSOCKETS_LIBRARY_DIRS}
|
|
||||||
)
|
|
||||||
|
|
||||||
set(LIBWEBSOCKETS_LIBRARIES ${LIBWEBSOCKETS_LIBRARY})
|
|
||||||
set(LIBWEBSOCKETS_INCLUDE_DIRS ${LIBWEBSOCKETS_INCLUDE_DIR})
|
|
||||||
|
|
||||||
include(FindPackageHandleStandardArgs)
|
|
||||||
# handle the QUIETLY and REQUIRED arguments and set LIBWEBSOCKETS_FOUND to TRUE
|
|
||||||
# if all listed variables are TRUE
|
|
||||||
find_package_handle_standard_args(LibWebSockets DEFAULT_MSG
|
|
||||||
LIBWEBSOCKETS_LIBRARY LIBWEBSOCKETS_INCLUDE_DIR
|
|
||||||
)
|
|
||||||
|
|
||||||
mark_as_advanced(
|
|
||||||
LIBWEBSOCKETS_LIBRARY
|
|
||||||
LIBWEBSOCKETS_INCLUDE_DIR
|
|
||||||
)
|
|
@ -39,7 +39,7 @@
|
|||||||
<ul id="nav_links" class="nav navbar-nav">
|
<ul id="nav_links" class="nav navbar-nav">
|
||||||
<li id="playlist"><a href="#/">Playlist</a></li>
|
<li id="playlist"><a href="#/">Playlist</a></li>
|
||||||
<li id="browse"><a href="#/browse/">Browse database</a></li>
|
<li id="browse"><a href="#/browse/">Browse database</a></li>
|
||||||
<li><a href="#" data-toggle="modal" data-target="#about" onclick="getVersion();">About</a></li>
|
<li><a href="#" data-toggle="modal" data-target="#about">About</a></li>
|
||||||
<li><a href="#" data-toggle="modal" data-target="#settings" onclick="getHost();">Settings</a></li>
|
<li><a href="#" data-toggle="modal" data-target="#settings" onclick="getHost();">Settings</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
@ -197,11 +197,23 @@
|
|||||||
<input type="text" class="form-control" id="mpdport" />
|
<input type="text" class="form-control" id="mpdport" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="form-group col-md-6">
|
||||||
|
<label class="control-label" for="mpd_pw">MPD Password</label>
|
||||||
|
<input type="password" class="form-control" id="mpd_pw" placeholder="Password"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group col-md-6">
|
||||||
|
<label class="control-label" for="mpd_pw_con">MPD Password (Confirmation)</label>
|
||||||
|
<input type="password" class="form-control" id="mpd_pw_con" placeholder="Password confirmation"
|
||||||
|
data-placement="right" data-toggle="popover" data-content="Password does not match!"
|
||||||
|
data-trigger="manual" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal" onclick="setHost();">Save</button>
|
<button type="button" class="btn btn-default" onclick="confirmSettings();">Save</button>
|
||||||
</div>
|
</div>
|
||||||
</div><!-- /.modal-content -->
|
</div><!-- /.modal-content -->
|
||||||
</div><!-- /.modal-dialog -->
|
</div><!-- /.modal-dialog -->
|
||||||
|
@ -41,7 +41,8 @@ var app = $.sammy(function() {
|
|||||||
current_app = 'playlist';
|
current_app = 'playlist';
|
||||||
$('#breadcrump').addClass('hide');
|
$('#breadcrump').addClass('hide');
|
||||||
$('#salamisandwich').find("tr:gt(0)").remove();
|
$('#salamisandwich').find("tr:gt(0)").remove();
|
||||||
$.get( "/api/get_playlist", socket.onmessage);
|
// $.get( "/api/get_playlist", socket.onmessage);
|
||||||
|
socket.send('MPD_API_GET_PLAYLIST');
|
||||||
|
|
||||||
$('#panel-heading').text("Playlist");
|
$('#panel-heading').text("Playlist");
|
||||||
$('#playlist').addClass('active');
|
$('#playlist').addClass('active');
|
||||||
@ -53,7 +54,8 @@ var app = $.sammy(function() {
|
|||||||
$('#salamisandwich').find("tr:gt(0)").remove();
|
$('#salamisandwich').find("tr:gt(0)").remove();
|
||||||
var path = this.params['splat'][0];
|
var path = this.params['splat'][0];
|
||||||
|
|
||||||
$.get( "/api/get_browse/" + encodeURIComponent(path), socket.onmessage);
|
// $.get( "/api/get_browse/" + encodeURIComponent(path), socket.onmessage);
|
||||||
|
socket.send('MPD_API_GET_BROWSE,' + path);
|
||||||
|
|
||||||
$('#panel-heading').text("Browse database: "+path+"");
|
$('#panel-heading').text("Browse database: "+path+"");
|
||||||
var path_array = path.split('/');
|
var path_array = path.split('/');
|
||||||
@ -100,13 +102,14 @@ $(document).ready(function(){
|
|||||||
|
|
||||||
function webSocketConnect() {
|
function webSocketConnect() {
|
||||||
if (typeof MozWebSocket != "undefined") {
|
if (typeof MozWebSocket != "undefined") {
|
||||||
socket = new MozWebSocket(get_appropriate_ws_url(), "ympd-client");
|
socket = new MozWebSocket(get_appropriate_ws_url());
|
||||||
} else {
|
} else {
|
||||||
socket = new WebSocket(get_appropriate_ws_url(), "ympd-client");
|
socket = new WebSocket(get_appropriate_ws_url());
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
socket.onopen = function() {
|
socket.onopen = function() {
|
||||||
|
console.log("connected");
|
||||||
$('.top-right').notify({
|
$('.top-right').notify({
|
||||||
message:{text:"Connected to ympd"},
|
message:{text:"Connected to ympd"},
|
||||||
fadeOut: { enabled: true, delay: 500 }
|
fadeOut: { enabled: true, delay: 500 }
|
||||||
@ -288,7 +291,7 @@ function webSocketConnect() {
|
|||||||
break;
|
break;
|
||||||
case "update_playlist":
|
case "update_playlist":
|
||||||
if(current_app === 'playlist')
|
if(current_app === 'playlist')
|
||||||
$.get( "/api/get_playlist", socket.onmessage);
|
socket.send('MPD_API_GET_PLAYLIST');
|
||||||
break;
|
break;
|
||||||
case "song_change":
|
case "song_change":
|
||||||
$('#currenttrack').text(" " + obj.data.title);
|
$('#currenttrack').text(" " + obj.data.title);
|
||||||
@ -315,6 +318,10 @@ function webSocketConnect() {
|
|||||||
case "mpdhost":
|
case "mpdhost":
|
||||||
$('#mpdhost').val(obj.data.host);
|
$('#mpdhost').val(obj.data.host);
|
||||||
$('#mpdport').val(obj.data.port);
|
$('#mpdport').val(obj.data.port);
|
||||||
|
if(obj.data.passwort_set) {
|
||||||
|
$('#mpd_pw').attr('placeholder', '*******');
|
||||||
|
$('#mpd_pw_con').attr('placeholder', '*******');
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case "error":
|
case "error":
|
||||||
$('.top-right').notify({
|
$('.top-right').notify({
|
||||||
@ -328,6 +335,7 @@ function webSocketConnect() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
socket.onclose = function(){
|
socket.onclose = function(){
|
||||||
|
console.log("disconnected");
|
||||||
$('.top-right').notify({
|
$('.top-right').notify({
|
||||||
message:{text:"Connection to ympd lost, retrying in 3 seconds "},
|
message:{text:"Connection to ympd lost, retrying in 3 seconds "},
|
||||||
type: "danger",
|
type: "danger",
|
||||||
@ -448,30 +456,36 @@ $('#btnnotify').on('click', function (e) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function getVersion()
|
|
||||||
{
|
|
||||||
$.get( "/api/get_version", function(response) {
|
|
||||||
$('#ympd_version').text(response.data.ympd_version);
|
|
||||||
$('#mpd_version').text(response.data.mpd_version);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getHost() {
|
function getHost() {
|
||||||
socket.send('MPD_API_GET_MPDHOST');
|
socket.send('MPD_API_GET_MPDHOST');
|
||||||
|
|
||||||
function onEnter(event) {
|
function onEnter(event) {
|
||||||
if ( event.which == 13 ) {
|
if ( event.which == 13 ) {
|
||||||
setHost();
|
confirmSettings();
|
||||||
$('#settings').modal('hide');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#mpdhost').keypress(onEnter);
|
$('#mpdhost').keypress(onEnter);
|
||||||
$('#mpdport').keypress(onEnter);
|
$('#mpdport').keypress(onEnter);
|
||||||
|
$('#mpd_pw').keypress(onEnter);
|
||||||
|
$('#mpd_pw_con').keypress(onEnter);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setHost() {
|
function confirmSettings() {
|
||||||
|
if($('#mpd_pw').val().length + $('#mpd_pw_con').val().length > 0) {
|
||||||
|
if ($('#mpd_pw').val() !== $('#mpd_pw_con').val())
|
||||||
|
{
|
||||||
|
$('#mpd_pw_con').popover('show');
|
||||||
|
setTimeout(function() {
|
||||||
|
$('#mpd_pw_con').popover('hide');
|
||||||
|
}, 2000);
|
||||||
|
return;
|
||||||
|
} else
|
||||||
|
socket.send('MPD_API_SET_MPDPASS,'+$('#mpd_pw').val());
|
||||||
|
}
|
||||||
socket.send('MPD_API_SET_MPDHOST,'+$('#mpdport').val()+','+$('#mpdhost').val());
|
socket.send('MPD_API_SET_MPDHOST,'+$('#mpdport').val()+','+$('#mpdhost').val());
|
||||||
|
$('#settings').modal('hide');
|
||||||
}
|
}
|
||||||
|
|
||||||
function notificationsSupported() {
|
function notificationsSupported() {
|
||||||
|
87
htdocs/mkdata.pl
Normal file
87
htdocs/mkdata.pl
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
# This program is used to embed arbitrary data into a C binary. It takes
|
||||||
|
# a list of files as an input, and produces a .c data file that contains
|
||||||
|
# contents of all these files as collection of char arrays.
|
||||||
|
#
|
||||||
|
# Usage: perl <this_file> <file1> [file2, ...] > embedded_data.c
|
||||||
|
|
||||||
|
use File::MimeInfo;
|
||||||
|
|
||||||
|
foreach my $i (0 .. $#ARGV) {
|
||||||
|
open FD, '<:raw', $ARGV[$i] or die "Cannot open $ARGV[$i]: $!\n";
|
||||||
|
printf("static const unsigned char v%d[] = {", $i);
|
||||||
|
my $byte;
|
||||||
|
my $j = 0;
|
||||||
|
while (read(FD, $byte, 1)) {
|
||||||
|
if (($j % 12) == 0) {
|
||||||
|
print "\n";
|
||||||
|
}
|
||||||
|
printf ' %#04x,', ord($byte);
|
||||||
|
$j++;
|
||||||
|
}
|
||||||
|
print " 0x00\n};\n";
|
||||||
|
close FD;
|
||||||
|
}
|
||||||
|
|
||||||
|
print <<EOS;
|
||||||
|
/* ympd
|
||||||
|
(c) 2013-2014 Andrew Karpow <andy\@ympd.org>
|
||||||
|
This project's homepage is: http://www.ympd.org
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
|
||||||
|
- Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
- Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
|
||||||
|
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __HTTP_FILES_H__
|
||||||
|
#define __HTTP_FILES_H__
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
static const struct embedded_file {
|
||||||
|
const char *name;
|
||||||
|
const unsigned char *data;
|
||||||
|
const char *mimetype;
|
||||||
|
size_t size;
|
||||||
|
} embedded_files[] = {
|
||||||
|
EOS
|
||||||
|
|
||||||
|
foreach my $i (0 .. $#ARGV) {
|
||||||
|
my $mime = mimetype($ARGV[$i]);
|
||||||
|
print " {\"/$ARGV[$i]\", v$i, \"$mime\", sizeof(v$i) - 1},\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
print <<EOS;
|
||||||
|
{NULL, NULL, NULL, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct embedded_file *find_embedded_file(const char *name) {
|
||||||
|
const struct embedded_file *p;
|
||||||
|
for (p = embedded_files; p->name != NULL; p++)
|
||||||
|
if (!strcmp(p->name, name))
|
||||||
|
return p;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
EOS
|
@ -26,159 +26,29 @@
|
|||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <libwebsockets.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <mpd/client.h>
|
|
||||||
|
|
||||||
#include "http_server.h"
|
#include "http_server.h"
|
||||||
#include "mpd_client.h"
|
#include "http_files.h"
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
char *resource_path = LOCAL_RESOURCE_PATH;
|
int callback_http(struct mg_connection *c)
|
||||||
extern enum mpd_conn_states mpd_conn_state;
|
|
||||||
|
|
||||||
struct serveable {
|
|
||||||
const char *urlpath;
|
|
||||||
const char *mimetype;
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct serveable whitelist[] = {
|
|
||||||
{ "/css/bootstrap.css", "text/css" },
|
|
||||||
{ "/css/mpd.css", "text/css" },
|
|
||||||
|
|
||||||
{ "/js/bootstrap.min.js", "text/javascript" },
|
|
||||||
{ "/js/mpd.js", "text/javascript" },
|
|
||||||
{ "/js/jquery-1.10.2.min.js", "text/javascript" },
|
|
||||||
{ "/js/jquery.cookie.js", "text/javascript" },
|
|
||||||
{ "/js/bootstrap-slider.js", "text/javascript" },
|
|
||||||
{ "/js/bootstrap-notify.js", "text/javascript" },
|
|
||||||
{ "/js/sammy.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"},
|
|
||||||
{ "/fonts/glyphicons-halflings-regular.eot", "application/vnd.ms-fontobject"},
|
|
||||||
|
|
||||||
{ "/assets/favicon.ico", "image/vnd.microsoft.icon" },
|
|
||||||
|
|
||||||
/* last one is the default served if no match */
|
|
||||||
{ "/index.html", "text/html" },
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char http_header[] = "HTTP/1.0 200 OK\x0d\x0a"
|
|
||||||
"Server: libwebsockets\x0d\x0a"
|
|
||||||
"Content-Type: application/json\x0d\x0a"
|
|
||||||
"Content-Length: 000000\x0d\x0a\x0d\x0a";
|
|
||||||
|
|
||||||
/* Converts a hex character to its integer value */
|
|
||||||
char from_hex(char ch) {
|
|
||||||
return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns a url-decoded version of str */
|
|
||||||
/* IMPORTANT: be sure to free() the returned string after use */
|
|
||||||
char *url_decode(char *str) {
|
|
||||||
char *pstr = str, *buf = malloc(strlen(str) + 1), *pbuf = buf;
|
|
||||||
while (*pstr) {
|
|
||||||
if (*pstr == '%') {
|
|
||||||
if (pstr[1] && pstr[2]) {
|
|
||||||
*pbuf++ = from_hex(pstr[1]) << 4 | from_hex(pstr[2]);
|
|
||||||
pstr += 2;
|
|
||||||
}
|
|
||||||
} else if (*pstr == '+') {
|
|
||||||
*pbuf++ = ' ';
|
|
||||||
} else {
|
|
||||||
*pbuf++ = *pstr;
|
|
||||||
}
|
|
||||||
pstr++;
|
|
||||||
}
|
|
||||||
*pbuf = '\0';
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
int callback_http(struct libwebsocket_context *context,
|
|
||||||
struct libwebsocket *wsi,
|
|
||||||
enum libwebsocket_callback_reasons reason, void *user,
|
|
||||||
void *in, size_t len)
|
|
||||||
{
|
{
|
||||||
char *response_buffer, *p;
|
const struct embedded_file *req_file;
|
||||||
char buf[64];
|
|
||||||
size_t n, response_size = 0;
|
|
||||||
|
|
||||||
switch (reason) {
|
if(!strcmp(c->uri, "/"))
|
||||||
case LWS_CALLBACK_HTTP:
|
req_file = find_embedded_file("/index.html");
|
||||||
if(in && strncmp((const char *)in, "/api/", 5) == 0)
|
else
|
||||||
{
|
req_file = find_embedded_file(c->uri);
|
||||||
|
|
||||||
p = (char *)malloc(MAX_SIZE + 100);
|
if(req_file)
|
||||||
memcpy(p, http_header, sizeof(http_header) - 1);
|
{
|
||||||
response_buffer = p + sizeof(http_header) - 1;
|
mg_send_header(c, "Content-Type", req_file->mimetype);
|
||||||
|
mg_send_data(c, req_file->data, req_file->size);
|
||||||
/* put content length and payload to buffer */
|
|
||||||
if(mpd_conn_state != MPD_CONNECTED) {}
|
return MG_REQUEST_PROCESSED;
|
||||||
else if(strncmp((const char *)in, "/api/get_browse", 15) == 0)
|
|
||||||
{
|
|
||||||
char *url;
|
|
||||||
if(sscanf(in, "/api/get_browse/%m[^\t\n]", &url) == 1)
|
|
||||||
{
|
|
||||||
char *url_decoded = url_decode(url);
|
|
||||||
response_size = mpd_put_browse(response_buffer, url_decoded);
|
|
||||||
free(url_decoded);
|
|
||||||
free(url);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
response_size = mpd_put_browse(response_buffer, "/");
|
|
||||||
|
|
||||||
}
|
|
||||||
else if(strncmp((const char *)in, "/api/get_playlist", 17) == 0)
|
|
||||||
response_size = mpd_put_playlist(response_buffer);
|
|
||||||
else if(strncmp((const char *)in, "/api/get_version", 16) == 0)
|
|
||||||
response_size = snprintf(response_buffer, MAX_SIZE,
|
|
||||||
"{\"type\":\"version\",\"data\":{"
|
|
||||||
"\"ympd_version\":\"%d.%d.%d\","
|
|
||||||
"\"mpd_version\":\"%d.%d.%d\""
|
|
||||||
"}}",
|
|
||||||
YMPD_VERSION_MAJOR, YMPD_VERSION_MINOR, YMPD_VERSION_PATCH,
|
|
||||||
LIBMPDCLIENT_MAJOR_VERSION, LIBMPDCLIENT_MINOR_VERSION,
|
|
||||||
LIBMPDCLIENT_PATCH_VERSION);
|
|
||||||
|
|
||||||
/* Copy size to content-length field */
|
|
||||||
sprintf(buf, "%6zu", response_size);
|
|
||||||
memcpy(p + sizeof(http_header) - 11, buf, 6);
|
|
||||||
|
|
||||||
n = libwebsocket_write(wsi, (unsigned char *)p,
|
|
||||||
sizeof(http_header) - 1 + response_size, LWS_WRITE_HTTP);
|
|
||||||
|
|
||||||
free(p);
|
|
||||||
/*
|
|
||||||
* book us a LWS_CALLBACK_HTTP_WRITEABLE callback
|
|
||||||
*/
|
|
||||||
libwebsocket_callback_on_writable(context, wsi);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (n = 0; n < (sizeof(whitelist) / sizeof(whitelist[0]) - 1); n++)
|
|
||||||
if (in && strcmp((const char *)in, whitelist[n].urlpath) == 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
sprintf(buf, "%s%s", resource_path, whitelist[n].urlpath);
|
|
||||||
|
|
||||||
if (libwebsockets_serve_http_file(context, wsi, buf, whitelist[n].mimetype, NULL))
|
|
||||||
return -1; /* through completion or error, close the socket */
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LWS_CALLBACK_HTTP_FILE_COMPLETION:
|
|
||||||
/* kill the connection after we sent one file */
|
|
||||||
return -1;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
mg_send_status(c, 404);
|
||||||
|
mg_printf_data(c, "Not Found");
|
||||||
|
return MG_REQUEST_PROCESSED;
|
||||||
}
|
}
|
||||||
|
@ -29,15 +29,9 @@
|
|||||||
#ifndef __HTTP_SERVER_H__
|
#ifndef __HTTP_SERVER_H__
|
||||||
#define __HTTP_SERVER_H__
|
#define __HTTP_SERVER_H__
|
||||||
|
|
||||||
#include <libwebsockets.h>
|
#include "mongoose.h"
|
||||||
|
|
||||||
struct per_session_data__http {
|
int callback_http(struct mg_connection *c);
|
||||||
int fd;
|
|
||||||
};
|
|
||||||
int callback_http(struct libwebsocket_context *context,
|
|
||||||
struct libwebsocket *wsi,
|
|
||||||
enum libwebsocket_callback_reasons reason, void *user,
|
|
||||||
void *in, size_t len);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
4296
src/mongoose.c
Normal file
4296
src/mongoose.c
Normal file
File diff suppressed because it is too large
Load Diff
125
src/mongoose.h
Normal file
125
src/mongoose.h
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
// Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
|
||||||
|
// Copyright (c) 2013-2014 Cesanta Software Limited
|
||||||
|
// All rights reserved
|
||||||
|
//
|
||||||
|
// This library is dual-licensed: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License version 2 as
|
||||||
|
// published by the Free Software Foundation. For the terms of this
|
||||||
|
// license, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
// You are free to use this library under the terms of the GNU General
|
||||||
|
// Public License, but WITHOUT ANY WARRANTY; without even the implied
|
||||||
|
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
// See the GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// Alternatively, you can license this library under a commercial
|
||||||
|
// license, as set out in <http://cesanta.com/>.
|
||||||
|
//
|
||||||
|
// NOTE: Detailed API documentation is at http://cesanta.com/#docs
|
||||||
|
|
||||||
|
#ifndef MONGOOSE_HEADER_INCLUDED
|
||||||
|
#define MONGOOSE_HEADER_INCLUDED
|
||||||
|
|
||||||
|
#define MONGOOSE_VERSION "5.3"
|
||||||
|
|
||||||
|
#include <stdio.h> // required for FILE
|
||||||
|
#include <stddef.h> // required for size_t
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
// This structure contains information about HTTP request.
|
||||||
|
struct mg_connection {
|
||||||
|
const char *request_method; // "GET", "POST", etc
|
||||||
|
const char *uri; // URL-decoded URI
|
||||||
|
const char *http_version; // E.g. "1.0", "1.1"
|
||||||
|
const char *query_string; // URL part after '?', not including '?', or NULL
|
||||||
|
|
||||||
|
char remote_ip[48]; // Max IPv6 string length is 45 characters
|
||||||
|
const char *local_ip; // Local IP address
|
||||||
|
unsigned short remote_port; // Client's port
|
||||||
|
unsigned short local_port; // Local port number
|
||||||
|
|
||||||
|
int num_headers; // Number of HTTP headers
|
||||||
|
struct mg_header {
|
||||||
|
const char *name; // HTTP header name
|
||||||
|
const char *value; // HTTP header value
|
||||||
|
} http_headers[30];
|
||||||
|
|
||||||
|
char *content; // POST (or websocket message) data, or NULL
|
||||||
|
size_t content_len; // content length
|
||||||
|
|
||||||
|
int is_websocket; // Connection is a websocket connection
|
||||||
|
int status_code; // HTTP status code for HTTP error handler
|
||||||
|
int wsbits; // First byte of the websocket frame
|
||||||
|
void *server_param; // Parameter passed to mg_add_uri_handler()
|
||||||
|
void *connection_param; // Placeholder for connection-specific data
|
||||||
|
void *callback_param; // Used by mg_iterate_over_connections()
|
||||||
|
};
|
||||||
|
|
||||||
|
struct mg_server; // Opaque structure describing server instance
|
||||||
|
typedef int (*mg_handler_t)(struct mg_connection *);
|
||||||
|
|
||||||
|
// Server management functions
|
||||||
|
struct mg_server *mg_create_server(void *server_param);
|
||||||
|
void mg_destroy_server(struct mg_server **);
|
||||||
|
const char *mg_set_option(struct mg_server *, const char *opt, const char *val);
|
||||||
|
unsigned int mg_poll_server(struct mg_server *, int milliseconds);
|
||||||
|
void mg_set_request_handler(struct mg_server *, mg_handler_t);
|
||||||
|
void mg_set_http_close_handler(struct mg_server *, mg_handler_t);
|
||||||
|
void mg_set_http_error_handler(struct mg_server *, mg_handler_t);
|
||||||
|
void mg_set_auth_handler(struct mg_server *, mg_handler_t);
|
||||||
|
const char **mg_get_valid_option_names(void);
|
||||||
|
const char *mg_get_option(const struct mg_server *server, const char *name);
|
||||||
|
void mg_set_listening_socket(struct mg_server *, int sock);
|
||||||
|
int mg_get_listening_socket(struct mg_server *);
|
||||||
|
void mg_iterate_over_connections(struct mg_server *, mg_handler_t, void *);
|
||||||
|
|
||||||
|
// Connection management functions
|
||||||
|
void mg_send_status(struct mg_connection *, int status_code);
|
||||||
|
void mg_send_header(struct mg_connection *, const char *name, const char *val);
|
||||||
|
void mg_send_data(struct mg_connection *, const void *data, int data_len);
|
||||||
|
void mg_printf_data(struct mg_connection *, const char *format, ...);
|
||||||
|
|
||||||
|
int mg_websocket_write(struct mg_connection *, int opcode,
|
||||||
|
const char *data, size_t data_len);
|
||||||
|
|
||||||
|
// Deprecated in favor of mg_send_* interface
|
||||||
|
int mg_write(struct mg_connection *, const void *buf, int len);
|
||||||
|
int mg_printf(struct mg_connection *conn, const char *fmt, ...);
|
||||||
|
|
||||||
|
const char *mg_get_header(const struct mg_connection *, const char *name);
|
||||||
|
const char *mg_get_mime_type(const char *name, const char *default_mime_type);
|
||||||
|
int mg_get_var(const struct mg_connection *conn, const char *var_name,
|
||||||
|
char *buf, size_t buf_len);
|
||||||
|
int mg_parse_header(const char *hdr, const char *var_name, char *buf, size_t);
|
||||||
|
int mg_parse_multipart(const char *buf, int buf_len,
|
||||||
|
char *var_name, int var_name_len,
|
||||||
|
char *file_name, int file_name_len,
|
||||||
|
const char **data, int *data_len);
|
||||||
|
|
||||||
|
// Utility functions
|
||||||
|
void *mg_start_thread(void *(*func)(void *), void *param);
|
||||||
|
char *mg_md5(char buf[33], ...);
|
||||||
|
int mg_authorize_digest(struct mg_connection *c, FILE *fp);
|
||||||
|
|
||||||
|
// Callback function return codes
|
||||||
|
enum { MG_REQUEST_NOT_PROCESSED, MG_REQUEST_PROCESSED, MG_REQUEST_CALL_AGAIN };
|
||||||
|
enum { MG_AUTH_FAIL, MG_AUTH_OK };
|
||||||
|
enum { MG_ERROR_NOT_PROCESSED, MG_ERROR_PROCESSED };
|
||||||
|
enum { MG_CLIENT_CONTINUE, MG_CLIENT_CLOSE };
|
||||||
|
|
||||||
|
// HTTP client events
|
||||||
|
enum {
|
||||||
|
MG_CONNECT_SUCCESS, MG_CONNECT_FAILURE,
|
||||||
|
MG_DOWNLOAD_SUCCESS, MG_DOWNLOAD_FAILURE
|
||||||
|
};
|
||||||
|
int mg_connect(struct mg_server *, const char *host, int port, int use_ssl,
|
||||||
|
mg_handler_t handler, void *param);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
#endif // MONGOOSE_HEADER_INCLUDED
|
485
src/mpd_client.c
485
src/mpd_client.c
@ -31,254 +31,273 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
|
|
||||||
#include <mpd/client.h>
|
#include <mpd/client.h>
|
||||||
#include <mpd/status.h>
|
|
||||||
#include <mpd/song.h>
|
|
||||||
#include <mpd/entity.h>
|
|
||||||
#include <mpd/search.h>
|
|
||||||
#include <mpd/tag.h>
|
|
||||||
|
|
||||||
#include "mpd_client.h"
|
#include "mpd_client.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
struct mpd_connection *conn = NULL;
|
const char * mpd_cmd_strs[] = {
|
||||||
enum mpd_conn_states mpd_conn_state = MPD_DISCONNECTED;
|
MPD_CMDS(GEN_STR)
|
||||||
enum mpd_state mpd_play_state = MPD_STATE_UNKNOWN;
|
};
|
||||||
|
|
||||||
int callback_ympd(struct libwebsocket_context *context,
|
static inline enum mpd_cmd_ids get_cmd_id(char *cmd)
|
||||||
struct libwebsocket *wsi,
|
|
||||||
enum libwebsocket_callback_reasons reason,
|
|
||||||
void *user, void *in, size_t len)
|
|
||||||
{
|
{
|
||||||
size_t n, m = -1;
|
for(int i = 0; i < sizeof(mpd_cmd_strs)/sizeof(mpd_cmd_strs[0]); i++)
|
||||||
char *buf = NULL, *p;
|
if(!strncmp(cmd, mpd_cmd_strs[i], strlen(mpd_cmd_strs[i])))
|
||||||
struct per_session_data__ympd *pss = (struct per_session_data__ympd *)user;
|
return i;
|
||||||
|
|
||||||
switch (reason) {
|
return -1;
|
||||||
case LWS_CALLBACK_ESTABLISHED:
|
}
|
||||||
lwsl_info("mpd_client: "
|
|
||||||
"LWS_CALLBACK_ESTABLISHED\n");
|
int callback_mpd(struct mg_connection *c)
|
||||||
|
{
|
||||||
|
enum mpd_cmd_ids cmd_id = get_cmd_id(c->content);
|
||||||
|
size_t n = 0;
|
||||||
|
unsigned int uint_buf, uint_buf_2;
|
||||||
|
int int_buf;
|
||||||
|
char *p_charbuf;
|
||||||
|
|
||||||
|
if(cmd_id == -1)
|
||||||
|
return MG_CLIENT_CONTINUE;
|
||||||
|
|
||||||
|
if(mpd.conn_state != MPD_CONNECTED && cmd_id != MPD_API_SET_MPDHOST &&
|
||||||
|
cmd_id != MPD_API_GET_MPDHOST && cmd_id != MPD_API_SET_MPDPASS)
|
||||||
|
return MG_CLIENT_CONTINUE;
|
||||||
|
|
||||||
|
switch(cmd_id)
|
||||||
|
{
|
||||||
|
case MPD_API_UPDATE_DB:
|
||||||
|
mpd_run_update(mpd.conn, NULL);
|
||||||
break;
|
break;
|
||||||
|
case MPD_API_SET_PAUSE:
|
||||||
case LWS_CALLBACK_SERVER_WRITEABLE:
|
mpd_run_toggle_pause(mpd.conn);
|
||||||
buf = (char *)malloc(MAX_SIZE + LWS_SEND_BUFFER_PRE_PADDING + LWS_SEND_BUFFER_POST_PADDING);
|
|
||||||
if(buf == NULL) {
|
|
||||||
lwsl_err("ERROR Failed allocating memory\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
|
|
||||||
|
|
||||||
if(pss->do_send & DO_SEND_ERROR) {
|
|
||||||
n = snprintf(p, MAX_SIZE, "{\"type\":\"error\", \"data\": \"%s\"}",
|
|
||||||
mpd_connection_get_error_message(conn));
|
|
||||||
pss->do_send &= ~DO_SEND_ERROR;
|
|
||||||
|
|
||||||
/* Try to recover error */
|
|
||||||
if (!mpd_connection_clear_error(conn))
|
|
||||||
mpd_conn_state = MPD_FAILURE;
|
|
||||||
}
|
|
||||||
else if(mpd_conn_state != MPD_CONNECTED) {
|
|
||||||
n = snprintf(p, MAX_SIZE, "{\"type\":\"disconnected\"}");
|
|
||||||
}
|
|
||||||
else if(pss->do_send & DO_SEND_PLAYLIST) {
|
|
||||||
/*n = mpd_put_playlist(p);*/
|
|
||||||
n = snprintf(p, MAX_SIZE, "{\"type\":\"update_playlist\"}");
|
|
||||||
pss->do_send &= ~DO_SEND_PLAYLIST;
|
|
||||||
}
|
|
||||||
else if(pss->do_send & DO_SEND_TRACK_INFO) {
|
|
||||||
n = mpd_put_current_song(p);
|
|
||||||
pss->do_send &= ~DO_SEND_TRACK_INFO;
|
|
||||||
}
|
|
||||||
else if(pss->do_send & DO_SEND_BROWSE) {
|
|
||||||
n = mpd_put_browse(p, pss->browse_path);
|
|
||||||
pss->do_send &= ~DO_SEND_BROWSE;
|
|
||||||
free(pss->browse_path);
|
|
||||||
}
|
|
||||||
else if(pss->do_send & DO_SEND_MPDHOST) {
|
|
||||||
n = snprintf(p, MAX_SIZE, "{\"type\":\"mpdhost\", \"data\": "
|
|
||||||
"{\"host\" : \"%s\", \"port\": \"%d\"}"
|
|
||||||
"}", mpd_host, mpd_port);
|
|
||||||
pss->do_send &= ~DO_SEND_MPDHOST;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* Default Action */
|
|
||||||
int current_song_id;
|
|
||||||
unsigned queue_version;
|
|
||||||
|
|
||||||
n = mpd_put_state(p, ¤t_song_id, &queue_version);
|
|
||||||
if(current_song_id != pss->current_song_id)
|
|
||||||
{
|
|
||||||
pss->current_song_id = current_song_id;
|
|
||||||
pss->do_send |= DO_SEND_TRACK_INFO;
|
|
||||||
libwebsocket_callback_on_writable(context, wsi);
|
|
||||||
}
|
|
||||||
else if(pss->queue_version != queue_version) {
|
|
||||||
pss->queue_version = queue_version;
|
|
||||||
pss->do_send |= DO_SEND_PLAYLIST;
|
|
||||||
libwebsocket_callback_on_writable(context, wsi);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(n > 0)
|
|
||||||
m = libwebsocket_write(wsi, (unsigned char *)p, n, LWS_WRITE_TEXT);
|
|
||||||
|
|
||||||
if (m < n) {
|
|
||||||
lwsl_err("ERROR %d writing to socket\n", n, m);
|
|
||||||
free(buf);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
free(buf);
|
|
||||||
break;
|
break;
|
||||||
|
case MPD_API_SET_PREV:
|
||||||
case LWS_CALLBACK_RECEIVE:
|
mpd_run_previous(mpd.conn);
|
||||||
if(!strcmp((const char *)in, MPD_API_GET_PLAYLIST))
|
break;
|
||||||
pss->do_send |= DO_SEND_PLAYLIST;
|
case MPD_API_SET_NEXT:
|
||||||
else if(!strcmp((const char *)in, MPD_API_GET_TRACK_INFO))
|
mpd_run_next(mpd.conn);
|
||||||
pss->do_send |= DO_SEND_TRACK_INFO;
|
break;
|
||||||
else if(!strcmp((const char *)in, MPD_API_UPDATE_DB))
|
case MPD_API_SET_PLAY:
|
||||||
mpd_run_update(conn, NULL);
|
mpd_run_play(mpd.conn);
|
||||||
else if(!strcmp((const char *)in, MPD_API_SET_PAUSE))
|
break;
|
||||||
mpd_run_toggle_pause(conn);
|
case MPD_API_SET_STOP:
|
||||||
else if(!strcmp((const char *)in, MPD_API_SET_PREV))
|
mpd_run_stop(mpd.conn);
|
||||||
mpd_run_previous(conn);
|
break;
|
||||||
else if(!strcmp((const char *)in, MPD_API_SET_NEXT))
|
case MPD_API_RM_ALL:
|
||||||
mpd_run_next(conn);
|
mpd_run_clear(mpd.conn);
|
||||||
else if(!strcmp((const char *)in, MPD_API_SET_PLAY))
|
break;
|
||||||
mpd_run_play(conn);
|
case MPD_API_GET_PLAYLIST:
|
||||||
else if(!strcmp((const char *)in, MPD_API_SET_STOP))
|
n = mpd_put_playlist(mpd.buf);
|
||||||
mpd_run_stop(conn);
|
break;
|
||||||
else if(!strcmp((const char *)in, MPD_API_RM_ALL))
|
case MPD_API_RM_TRACK:
|
||||||
mpd_run_clear(conn);
|
if(sscanf(c->content, "MPD_API_RM_TRACK,%u", &uint_buf))
|
||||||
else if(!strncmp((const char *)in, MPD_API_RM_TRACK, sizeof(MPD_API_RM_TRACK)-1)) {
|
mpd_run_delete_id(mpd.conn, uint_buf);
|
||||||
unsigned id;
|
break;
|
||||||
if(sscanf(in, "MPD_API_RM_TRACK,%u", &id))
|
case MPD_API_PLAY_TRACK:
|
||||||
mpd_run_delete_id(conn, id);
|
if(sscanf(c->content, "MPD_API_PLAY_TRACK,%u", &uint_buf))
|
||||||
|
mpd_run_play_id(mpd.conn, uint_buf);
|
||||||
libwebsocket_callback_on_writable(context, wsi);
|
break;
|
||||||
|
case MPD_API_TOGGLE_RANDOM:
|
||||||
|
if(sscanf(c->content, "MPD_API_TOGGLE_RANDOM,%u", &uint_buf))
|
||||||
|
mpd_run_random(mpd.conn, uint_buf);
|
||||||
|
break;
|
||||||
|
case MPD_API_TOGGLE_REPEAT:
|
||||||
|
if(sscanf(c->content, "MPD_API_TOGGLE_REPEAT,%u", &uint_buf))
|
||||||
|
mpd_run_repeat(mpd.conn, uint_buf);
|
||||||
|
break;
|
||||||
|
case MPD_API_TOGGLE_CONSUME:
|
||||||
|
if(sscanf(c->content, "MPD_API_TOGGLE_CONSUME,%u", &uint_buf))
|
||||||
|
mpd_run_consume(mpd.conn, uint_buf);
|
||||||
|
break;
|
||||||
|
case MPD_API_TOGGLE_SINGLE:
|
||||||
|
if(sscanf(c->content, "MPD_API_TOGGLE_SINGLE,%u", &uint_buf))
|
||||||
|
mpd_run_single(mpd.conn, uint_buf);
|
||||||
|
break;
|
||||||
|
case MPD_API_SET_VOLUME:
|
||||||
|
if(sscanf(c->content, "MPD_API_SET_VOLUME,%ud", &uint_buf) && uint_buf <= 100)
|
||||||
|
mpd_run_set_volume(mpd.conn, uint_buf);
|
||||||
|
break;
|
||||||
|
case MPD_API_SET_SEEK:
|
||||||
|
if(sscanf(c->content, "MPD_API_SET_SEEK,%u,%u", &uint_buf, &uint_buf_2))
|
||||||
|
mpd_run_seek_id(mpd.conn, uint_buf, uint_buf_2);
|
||||||
|
break;
|
||||||
|
case MPD_API_GET_BROWSE:
|
||||||
|
if(sscanf(c->content, "MPD_API_GET_BROWSE,%m[^\t\n]", &p_charbuf) && p_charbuf != NULL)
|
||||||
|
n = mpd_put_browse(mpd.buf, p_charbuf);
|
||||||
|
else
|
||||||
|
n = mpd_put_browse(mpd.buf, "/");
|
||||||
|
free(p_charbuf);
|
||||||
|
break;
|
||||||
|
case MPD_API_ADD_TRACK:
|
||||||
|
if(sscanf(c->content, "MPD_API_ADD_TRACK,%m[^\t\n]", &p_charbuf) && p_charbuf != NULL)
|
||||||
|
{
|
||||||
|
mpd_run_add(mpd.conn, p_charbuf);
|
||||||
|
free(p_charbuf);
|
||||||
}
|
}
|
||||||
else if(!strncmp((const char *)in, MPD_API_PLAY_TRACK, sizeof(MPD_API_PLAY_TRACK)-1)) {
|
break;
|
||||||
unsigned id;
|
case MPD_API_ADD_PLAY_TRACK:
|
||||||
if(sscanf(in, "MPD_API_PLAY_TRACK,%u", &id))
|
if(sscanf(c->content, "MPD_API_ADD_PLAY_TRACK,%m[^\t\n]", &p_charbuf) && p_charbuf != NULL)
|
||||||
mpd_run_play_id(conn, id);
|
{
|
||||||
}
|
int_buf = mpd_run_add_id(mpd.conn, p_charbuf);
|
||||||
else if(!strncmp((const char *)in, MPD_API_TOGGLE_RANDOM, sizeof(MPD_API_TOGGLE_RANDOM)-1)) {
|
if(int_buf != -1)
|
||||||
unsigned random;
|
mpd_run_play_id(mpd.conn, int_buf);
|
||||||
if(sscanf(in, "MPD_API_TOGGLE_RANDOM,%u", &random))
|
free(p_charbuf);
|
||||||
mpd_run_random(conn, random);
|
|
||||||
}
|
|
||||||
else if(!strncmp((const char *)in, MPD_API_TOGGLE_REPEAT, sizeof(MPD_API_TOGGLE_REPEAT)-1)) {
|
|
||||||
unsigned repeat;
|
|
||||||
if(sscanf(in, "MPD_API_TOGGLE_REPEAT,%u", &repeat))
|
|
||||||
mpd_run_repeat(conn, repeat);
|
|
||||||
}
|
|
||||||
else if(!strncmp((const char *)in, MPD_API_TOGGLE_CONSUME, sizeof(MPD_API_TOGGLE_CONSUME)-1)) {
|
|
||||||
unsigned consume;
|
|
||||||
if(sscanf(in, "MPD_API_TOGGLE_CONSUME,%u", &consume))
|
|
||||||
mpd_run_consume(conn, consume);
|
|
||||||
}
|
|
||||||
else if(!strncmp((const char *)in, MPD_API_TOGGLE_SINGLE, sizeof(MPD_API_TOGGLE_SINGLE)-1)) {
|
|
||||||
unsigned single;
|
|
||||||
if(sscanf(in, "MPD_API_TOGGLE_SINGLE,%u", &single))
|
|
||||||
mpd_run_single(conn, single);
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
mpd_run_set_volume(conn, volume);
|
|
||||||
}
|
|
||||||
else if(!strncmp((const char *)in, MPD_API_SET_SEEK, sizeof(MPD_API_SET_SEEK)-1)) {
|
|
||||||
unsigned int seek, songid;
|
|
||||||
if(sscanf(in, "MPD_API_SET_SEEK,%u,%u", &songid, &seek)) {
|
|
||||||
mpd_run_seek_id(conn, songid, seek);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(!strncmp((const char *)in, MPD_API_GET_BROWSE, sizeof(MPD_API_GET_BROWSE)-1)) {
|
|
||||||
char *dir;
|
|
||||||
if(sscanf(in, "MPD_API_GET_BROWSE,%m[^\t\n]", &dir) && dir != NULL) {
|
|
||||||
pss->do_send |= DO_SEND_BROWSE;
|
|
||||||
pss->browse_path = dir;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(!strncmp((const char *)in, MPD_API_ADD_TRACK, sizeof(MPD_API_ADD_TRACK)-1)) {
|
|
||||||
char *uri;
|
|
||||||
if(sscanf(in, "MPD_API_ADD_TRACK,%m[^\t\n]", &uri) && uri != NULL) {
|
|
||||||
mpd_run_add(conn, uri);
|
|
||||||
free(uri);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(!strncmp((const char *)in, MPD_API_ADD_PLAY_TRACK, sizeof(MPD_API_ADD_PLAY_TRACK)-1)) {
|
|
||||||
char *uri;
|
|
||||||
if(sscanf(in, "MPD_API_ADD_PLAY_TRACK,%m[^\t\n]", &uri) && uri != NULL) {
|
|
||||||
int added_song = mpd_run_add_id(conn, uri);
|
|
||||||
if(added_song != -1)
|
|
||||||
mpd_run_play_id(conn, added_song);
|
|
||||||
free(uri);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#ifdef WITH_MPD_HOST_CHANGE
|
#ifdef WITH_MPD_HOST_CHANGE
|
||||||
else if(!strncmp((const char *)in, MPD_API_SET_MPDHOST, sizeof(MPD_API_SET_MPDHOST)-1)) {
|
/* Commands allowed when disconnected from MPD server */
|
||||||
char *host;
|
case MPD_API_SET_MPDHOST:
|
||||||
int port = 0;
|
int_buf = 0;
|
||||||
if(sscanf(in, "MPD_API_SET_MPDHOST,%d,%m[^\t\n ]", &port, &host) && host != NULL && port > 0) {
|
if(sscanf(c->content, "MPD_API_SET_MPDHOST,%d,%m[^\t\n ]", &int_buf, &p_charbuf) &&
|
||||||
strncpy(mpd_host, host, sizeof(mpd_host));
|
p_charbuf != NULL && int_buf > 0)
|
||||||
free(host);
|
{
|
||||||
mpd_port = port;
|
strncpy(mpd.host, p_charbuf, sizeof(mpd.host));
|
||||||
mpd_conn_state = MPD_RECONNECT;
|
free(p_charbuf);
|
||||||
break;
|
mpd.port = int_buf;
|
||||||
}
|
mpd.conn_state = MPD_RECONNECT;
|
||||||
|
return MG_CLIENT_CONTINUE;
|
||||||
}
|
}
|
||||||
else if(!strncmp((const char *)in, MPD_API_GET_MPDHOST, sizeof(MPD_API_GET_MPDHOST)-1)) {
|
break;
|
||||||
pss->do_send |= DO_SEND_MPDHOST;
|
case MPD_API_GET_MPDHOST:
|
||||||
|
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"mpdhost\", \"data\": "
|
||||||
|
"{\"host\" : \"%s\", \"port\": \"%d\", \"passwort_set\": %s}"
|
||||||
|
"}", mpd.host, mpd.port, mpd.password ? "true" : "false");
|
||||||
|
printf("mpd_password is %p\n", mpd.password);
|
||||||
|
case MPD_API_SET_MPDPASS:
|
||||||
|
if(sscanf(c->content, "MPD_API_SET_MPDPASS,%m[^\t\n ]", &p_charbuf) &&
|
||||||
|
p_charbuf != NULL)
|
||||||
|
{
|
||||||
|
if(mpd.password)
|
||||||
|
free(mpd.password);
|
||||||
|
|
||||||
|
mpd.password = p_charbuf;
|
||||||
|
mpd.conn_state = MPD_RECONNECT;
|
||||||
|
printf("Got mpd pw %s\n", mpd.password);
|
||||||
|
return MG_CLIENT_CONTINUE;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if(mpd_conn_state == MPD_CONNECTED && mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS)
|
|
||||||
pss->do_send |= DO_SEND_ERROR;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(mpd.conn_state == MPD_CONNECTED && mpd_connection_get_error(mpd.conn) != MPD_ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"error\", \"data\": \"%s\"}",
|
||||||
|
mpd_connection_get_error_message(mpd.conn));
|
||||||
|
|
||||||
|
/* Try to recover error */
|
||||||
|
if (!mpd_connection_clear_error(mpd.conn))
|
||||||
|
mpd.conn_state = MPD_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(n > 0)
|
||||||
|
mg_websocket_write(c, 1, mpd.buf, n);
|
||||||
|
|
||||||
|
return MG_CLIENT_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mpd_close_handler(struct mg_connection *c)
|
||||||
|
{
|
||||||
|
/* Cleanup session data */
|
||||||
|
if(c->connection_param)
|
||||||
|
free(c->connection_param);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mpd_loop()
|
static int mpd_notify_callback(struct mg_connection *c) {
|
||||||
|
size_t n;
|
||||||
|
|
||||||
|
if(!c->is_websocket)
|
||||||
|
return MG_REQUEST_PROCESSED;
|
||||||
|
|
||||||
|
if(c->callback_param)
|
||||||
|
{
|
||||||
|
/* error message? */
|
||||||
|
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"error\",\"data\":\"%s\"}",
|
||||||
|
(const char *)c->callback_param);
|
||||||
|
|
||||||
|
mg_websocket_write(c, 1, mpd.buf, n);
|
||||||
|
return MG_REQUEST_PROCESSED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!c->connection_param)
|
||||||
|
c->connection_param = calloc(1, sizeof(struct t_mpd_client_session));
|
||||||
|
|
||||||
|
struct t_mpd_client_session *s = (struct t_mpd_client_session *)c->connection_param;
|
||||||
|
|
||||||
|
if(mpd.conn_state != MPD_CONNECTED) {
|
||||||
|
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"disconnected\"}");
|
||||||
|
mg_websocket_write(c, 1, mpd.buf, n);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mg_websocket_write(c, 1, mpd.buf, mpd.buf_size);
|
||||||
|
|
||||||
|
if(s->song_id != mpd.song_id)
|
||||||
|
{
|
||||||
|
n = mpd_put_current_song(mpd.buf);
|
||||||
|
mg_websocket_write(c, 1, mpd.buf, n);
|
||||||
|
s->song_id = mpd.song_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(s->queue_version != mpd.queue_version)
|
||||||
|
{
|
||||||
|
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"update_playlist\"}");
|
||||||
|
mg_websocket_write(c, 1, mpd.buf, n);
|
||||||
|
s->queue_version = mpd.queue_version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return MG_REQUEST_PROCESSED;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mpd_poll(struct mg_server *s)
|
||||||
{
|
{
|
||||||
switch (mpd_conn_state) {
|
switch (mpd.conn_state) {
|
||||||
case MPD_DISCONNECTED:
|
case MPD_DISCONNECTED:
|
||||||
/* Try to connect */
|
/* Try to connect */
|
||||||
lwsl_notice("MPD Connecting to %s:%d\n", mpd_host, mpd_port);
|
fprintf(stdout, "MPD Connecting to %s:%d\n", mpd.host, mpd.port);
|
||||||
conn = mpd_connection_new(mpd_host, mpd_port, 3000);
|
mpd.conn = mpd_connection_new(mpd.host, mpd.port, 3000);
|
||||||
if (conn == NULL) {
|
if (mpd.conn == NULL) {
|
||||||
lwsl_err("Out of memory.");
|
fprintf(stderr, "Out of memory.");
|
||||||
mpd_conn_state = MPD_FAILURE;
|
mpd.conn_state = MPD_FAILURE;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS) {
|
if (mpd_connection_get_error(mpd.conn) != MPD_ERROR_SUCCESS) {
|
||||||
lwsl_err("MPD connection: %s\n", mpd_connection_get_error_message(conn));
|
fprintf(stderr, "MPD connection: %s\n", mpd_connection_get_error_message(mpd.conn));
|
||||||
mpd_conn_state = MPD_FAILURE;
|
mg_iterate_over_connections(s, mpd_notify_callback,
|
||||||
|
(void *)mpd_connection_get_error_message(mpd.conn));
|
||||||
|
mpd.conn_state = MPD_FAILURE;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
lwsl_notice("MPD connected.\n");
|
if(mpd.password && !mpd_run_password(mpd.conn, mpd.password))
|
||||||
mpd_conn_state = MPD_CONNECTED;
|
{
|
||||||
|
fprintf(stderr, "MPD connection: %s\n", mpd_connection_get_error_message(mpd.conn));
|
||||||
|
mg_iterate_over_connections(s, mpd_notify_callback,
|
||||||
|
(void *)mpd_connection_get_error_message(mpd.conn));
|
||||||
|
mpd.conn_state = MPD_FAILURE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "MPD connected.\n");
|
||||||
|
mpd.conn_state = MPD_CONNECTED;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MPD_FAILURE:
|
case MPD_FAILURE:
|
||||||
lwsl_err("MPD connection failed.\n");
|
fprintf(stderr, "MPD connection failed.\n");
|
||||||
|
|
||||||
|
case MPD_DISCONNECT:
|
||||||
case MPD_RECONNECT:
|
case MPD_RECONNECT:
|
||||||
if(conn != NULL)
|
if(mpd.conn != NULL)
|
||||||
mpd_connection_free(conn);
|
mpd_connection_free(mpd.conn);
|
||||||
conn = NULL;
|
mpd.conn = NULL;
|
||||||
mpd_conn_state = MPD_DISCONNECTED;
|
mpd.conn_state = MPD_DISCONNECTED;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MPD_CONNECTED:
|
case MPD_CONNECTED:
|
||||||
/* Nothing to do */
|
mpd.buf_size = mpd_put_state(mpd.buf, &mpd.song_id, &mpd.queue_version);
|
||||||
|
mg_iterate_over_connections(s, mpd_notify_callback, NULL);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -308,10 +327,10 @@ int mpd_put_state(char *buffer, int *current_song_id, unsigned *queue_version)
|
|||||||
struct mpd_status *status;
|
struct mpd_status *status;
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
status = mpd_run_status(conn);
|
status = mpd_run_status(mpd.conn);
|
||||||
if (!status) {
|
if (!status) {
|
||||||
lwsl_err("MPD mpd_run_status: %s\n", mpd_connection_get_error_message(conn));
|
fprintf(stderr, "MPD mpd_run_status: %s\n", mpd_connection_get_error_message(mpd.conn));
|
||||||
mpd_conn_state = MPD_FAILURE;
|
mpd.conn_state = MPD_FAILURE;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -345,7 +364,7 @@ int mpd_put_current_song(char *buffer)
|
|||||||
const char *end = buffer + MAX_SIZE;
|
const char *end = buffer + MAX_SIZE;
|
||||||
struct mpd_song *song;
|
struct mpd_song *song;
|
||||||
|
|
||||||
song = mpd_run_current_song(conn);
|
song = mpd_run_current_song(mpd.conn);
|
||||||
if(song == NULL)
|
if(song == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@ -362,7 +381,7 @@ int mpd_put_current_song(char *buffer)
|
|||||||
|
|
||||||
cur += snprintf(cur, end - cur, "}}");
|
cur += snprintf(cur, end - cur, "}}");
|
||||||
mpd_song_free(song);
|
mpd_song_free(song);
|
||||||
mpd_response_finish(conn);
|
mpd_response_finish(mpd.conn);
|
||||||
|
|
||||||
return cur - buffer;
|
return cur - buffer;
|
||||||
}
|
}
|
||||||
@ -373,19 +392,19 @@ int mpd_put_playlist(char *buffer)
|
|||||||
const char *end = buffer + MAX_SIZE;
|
const char *end = buffer + MAX_SIZE;
|
||||||
struct mpd_entity *entity;
|
struct mpd_entity *entity;
|
||||||
|
|
||||||
if (!mpd_send_list_queue_meta(conn)) {
|
if (!mpd_send_list_queue_meta(mpd.conn)) {
|
||||||
lwsl_err("MPD mpd_send_list_queue_meta: %s\n", mpd_connection_get_error_message(conn));
|
fprintf(stderr, "MPD mpd_send_list_queue_meta: %s\n", mpd_connection_get_error_message(mpd.conn));
|
||||||
cur += snprintf(cur, end - cur, "{\"type\":\"error\",\"data\":\"%s\"}",
|
cur += snprintf(cur, end - cur, "{\"type\":\"error\",\"data\":\"%s\"}",
|
||||||
mpd_connection_get_error_message(conn));
|
mpd_connection_get_error_message(mpd.conn));
|
||||||
|
|
||||||
if (!mpd_connection_clear_error(conn))
|
if (!mpd_connection_clear_error(mpd.conn))
|
||||||
mpd_conn_state = MPD_FAILURE;
|
mpd.conn_state = MPD_FAILURE;
|
||||||
return cur - buffer;
|
return cur - buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
cur += snprintf(cur, end - cur, "{\"type\": \"playlist\", \"data\": [ ");
|
cur += snprintf(cur, end - cur, "{\"type\": \"playlist\", \"data\": [ ");
|
||||||
|
|
||||||
while((entity = mpd_recv_entity(conn)) != NULL) {
|
while((entity = mpd_recv_entity(mpd.conn)) != NULL) {
|
||||||
const struct mpd_song *song;
|
const struct mpd_song *song;
|
||||||
|
|
||||||
if(mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG) {
|
if(mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG) {
|
||||||
@ -413,18 +432,18 @@ int mpd_put_browse(char *buffer, char *path)
|
|||||||
const char *end = buffer + MAX_SIZE;
|
const char *end = buffer + MAX_SIZE;
|
||||||
struct mpd_entity *entity;
|
struct mpd_entity *entity;
|
||||||
|
|
||||||
if (!mpd_send_list_meta(conn, path)) {
|
if (!mpd_send_list_meta(mpd.conn, path)) {
|
||||||
lwsl_err("MPD mpd_send_list_meta: %s\n", mpd_connection_get_error_message(conn));
|
fprintf(stderr, "MPD mpd_send_list_meta: %s\n", mpd_connection_get_error_message(mpd.conn));
|
||||||
cur += snprintf(cur, end - cur, "{\"type\":\"error\",\"data\":\"%s\"}",
|
cur += snprintf(cur, end - cur, "{\"type\":\"error\",\"data\":\"%s\"}",
|
||||||
mpd_connection_get_error_message(conn));
|
mpd_connection_get_error_message(mpd.conn));
|
||||||
|
|
||||||
if (!mpd_connection_clear_error(conn))
|
if (!mpd_connection_clear_error(mpd.conn))
|
||||||
mpd_conn_state = MPD_FAILURE;
|
mpd.conn_state = MPD_FAILURE;
|
||||||
return cur - buffer;
|
return cur - buffer;
|
||||||
}
|
}
|
||||||
cur += snprintf(cur, end - cur, "{\"type\":\"browse\",\"data\":[ ");
|
cur += snprintf(cur, end - cur, "{\"type\":\"browse\",\"data\":[ ");
|
||||||
|
|
||||||
while((entity = mpd_recv_entity(conn)) != NULL) {
|
while((entity = mpd_recv_entity(mpd.conn)) != NULL) {
|
||||||
const struct mpd_song *song;
|
const struct mpd_song *song;
|
||||||
const struct mpd_directory *dir;
|
const struct mpd_directory *dir;
|
||||||
const struct mpd_playlist *pl;
|
const struct mpd_playlist *pl;
|
||||||
@ -463,9 +482,9 @@ int mpd_put_browse(char *buffer, char *path)
|
|||||||
mpd_entity_free(entity);
|
mpd_entity_free(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS || !mpd_response_finish(conn)) {
|
if (mpd_connection_get_error(mpd.conn) != MPD_ERROR_SUCCESS || !mpd_response_finish(mpd.conn)) {
|
||||||
lwsl_err("MPD mpd_send_list_meta: %s\n", mpd_connection_get_error_message(conn));
|
fprintf(stderr, "MPD mpd_send_list_meta: %s\n", mpd_connection_get_error_message(mpd.conn));
|
||||||
mpd_conn_state = MPD_FAILURE;
|
mpd.conn_state = MPD_FAILURE;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -474,3 +493,9 @@ int mpd_put_browse(char *buffer, char *path)
|
|||||||
cur += snprintf(cur, end - cur, "] }");
|
cur += snprintf(cur, end - cur, "] }");
|
||||||
return cur - buffer;
|
return cur - buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void mpd_disconnect()
|
||||||
|
{
|
||||||
|
mpd.conn_state = MPD_DISCONNECT;
|
||||||
|
mpd_poll(NULL);
|
||||||
|
}
|
105
src/mpd_client.h
105
src/mpd_client.h
@ -29,68 +29,75 @@
|
|||||||
#ifndef __MPD_CLIENT_H__
|
#ifndef __MPD_CLIENT_H__
|
||||||
#define __MPD_CLIENT_H__
|
#define __MPD_CLIENT_H__
|
||||||
|
|
||||||
#include <libwebsockets.h>
|
#include "mongoose.h"
|
||||||
|
|
||||||
#define MAX_SIZE 1024 * 100
|
#define MAX_SIZE 1024 * 100
|
||||||
|
#define GEN_ENUM(X) X,
|
||||||
|
#define GEN_STR(X) #X,
|
||||||
|
#define MPD_CMDS(X) \
|
||||||
|
X(MPD_API_GET_PLAYLIST) \
|
||||||
|
X(MPD_API_GET_BROWSE) \
|
||||||
|
X(MPD_API_GET_MPDHOST) \
|
||||||
|
X(MPD_API_ADD_TRACK) \
|
||||||
|
X(MPD_API_ADD_PLAY_TRACK) \
|
||||||
|
X(MPD_API_PLAY_TRACK) \
|
||||||
|
X(MPD_API_RM_TRACK) \
|
||||||
|
X(MPD_API_RM_ALL) \
|
||||||
|
X(MPD_API_SET_VOLUME) \
|
||||||
|
X(MPD_API_SET_PAUSE) \
|
||||||
|
X(MPD_API_SET_PLAY) \
|
||||||
|
X(MPD_API_SET_STOP) \
|
||||||
|
X(MPD_API_SET_SEEK) \
|
||||||
|
X(MPD_API_SET_NEXT) \
|
||||||
|
X(MPD_API_SET_PREV) \
|
||||||
|
X(MPD_API_SET_MPDHOST) \
|
||||||
|
X(MPD_API_SET_MPDPASS) \
|
||||||
|
X(MPD_API_UPDATE_DB) \
|
||||||
|
X(MPD_API_TOGGLE_RANDOM) \
|
||||||
|
X(MPD_API_TOGGLE_CONSUME) \
|
||||||
|
X(MPD_API_TOGGLE_SINGLE) \
|
||||||
|
X(MPD_API_TOGGLE_REPEAT)
|
||||||
|
|
||||||
#define DO_SEND_STATE (1 << 0)
|
enum mpd_cmd_ids {
|
||||||
#define DO_SEND_PLAYLIST (1 << 1)
|
MPD_CMDS(GEN_ENUM)
|
||||||
#define DO_SEND_TRACK_INFO (1 << 2)
|
|
||||||
#define DO_SEND_BROWSE (1 << 3)
|
|
||||||
#define DO_SEND_ERROR (1 << 4)
|
|
||||||
#define DO_SEND_MPDHOST (1 << 5)
|
|
||||||
|
|
||||||
#define MPD_API_GET_SEEK "MPD_API_GET_SEEK"
|
|
||||||
#define MPD_API_GET_PLAYLIST "MPD_API_GET_PLAYLIST"
|
|
||||||
#define MPD_API_GET_TRACK_INFO "MPD_API_GET_TRACK_INFO"
|
|
||||||
#define MPD_API_GET_BROWSE "MPD_API_GET_BROWSE"
|
|
||||||
#define MPD_API_GET_MPDHOST "MPD_API_GET_MPDHOST"
|
|
||||||
#define MPD_API_ADD_TRACK "MPD_API_ADD_TRACK"
|
|
||||||
#define MPD_API_ADD_PLAY_TRACK "MPD_API_ADD_PLAY_TRACK"
|
|
||||||
#define MPD_API_PLAY_TRACK "MPD_API_PLAY_TRACK"
|
|
||||||
#define MPD_API_RM_TRACK "MPD_API_RM_TRACK"
|
|
||||||
#define MPD_API_RM_ALL "MPD_API_RM_ALL"
|
|
||||||
#define MPD_API_SET_VOLUME "MPD_API_SET_VOLUME"
|
|
||||||
#define MPD_API_SET_PAUSE "MPD_API_SET_PAUSE"
|
|
||||||
#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"
|
|
||||||
#define MPD_API_SET_MPDHOST "MPD_API_SET_MPDHOST"
|
|
||||||
#define MPD_API_UPDATE_DB "MPD_API_UPDATE_DB"
|
|
||||||
#define MPD_API_TOGGLE_RANDOM "MPD_API_TOGGLE_RANDOM"
|
|
||||||
#define MPD_API_TOGGLE_CONSUME "MPD_API_TOGGLE_CONSUME"
|
|
||||||
#define MPD_API_TOGGLE_SINGLE "MPD_API_TOGGLE_SINGLE"
|
|
||||||
#define MPD_API_TOGGLE_REPEAT "MPD_API_TOGGLE_REPEAT"
|
|
||||||
|
|
||||||
struct per_session_data__ympd {
|
|
||||||
int do_send;
|
|
||||||
unsigned queue_version;
|
|
||||||
int current_song_id;
|
|
||||||
char *browse_path;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum mpd_conn_states {
|
enum mpd_conn_states {
|
||||||
MPD_FAILURE,
|
|
||||||
MPD_DISCONNECTED,
|
MPD_DISCONNECTED,
|
||||||
|
MPD_FAILURE,
|
||||||
MPD_CONNECTED,
|
MPD_CONNECTED,
|
||||||
MPD_RECONNECT
|
MPD_RECONNECT,
|
||||||
|
MPD_DISCONNECT
|
||||||
};
|
};
|
||||||
|
|
||||||
void *mpd_idle_connection(void *_data);
|
struct t_mpd {
|
||||||
int callback_ympd(struct libwebsocket_context *context,
|
int port;
|
||||||
struct libwebsocket *wsi,
|
char host[128];
|
||||||
enum libwebsocket_callback_reasons reason,
|
char *password;
|
||||||
void *user, void *in, size_t len);
|
|
||||||
void mpd_loop();
|
struct mpd_connection *conn;
|
||||||
|
enum mpd_conn_states conn_state;
|
||||||
|
|
||||||
|
/* Reponse Buffer */
|
||||||
|
char buf[MAX_SIZE];
|
||||||
|
size_t buf_size;
|
||||||
|
|
||||||
|
int song_id;
|
||||||
|
unsigned queue_version;
|
||||||
|
} mpd;
|
||||||
|
|
||||||
|
struct t_mpd_client_session {
|
||||||
|
int song_id;
|
||||||
|
unsigned queue_version;
|
||||||
|
};
|
||||||
|
|
||||||
|
void mpd_poll(struct mg_server *s);
|
||||||
|
int callback_mpd(struct mg_connection *c);
|
||||||
|
int mpd_close_handler(struct mg_connection *c);
|
||||||
int mpd_put_state(char *buffer, int *current_song_id, unsigned *queue_version);
|
int mpd_put_state(char *buffer, int *current_song_id, unsigned *queue_version);
|
||||||
int mpd_put_current_song(char *buffer);
|
int mpd_put_current_song(char *buffer);
|
||||||
int mpd_put_playlist(char *buffer);
|
int mpd_put_playlist(char *buffer);
|
||||||
int mpd_put_browse(char *buffer, char *path);
|
int mpd_put_browse(char *buffer, char *path);
|
||||||
|
void mpd_disconnect();
|
||||||
int mpd_port;
|
|
||||||
char mpd_host[255];
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
/*
|
|
||||||
from http://www.geekhideout.com/urlcode.shtml
|
|
||||||
public domain
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
/* Converts a hex character to its integer value */
|
|
||||||
char from_hex(char ch) {
|
|
||||||
return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Converts an integer value to its hex character*/
|
|
||||||
char to_hex(char code) {
|
|
||||||
static char hex[] = "0123456789abcdef";
|
|
||||||
return hex[code & 15];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns a url-decoded version of str */
|
|
||||||
/* IMPORTANT: be sure to free() the returned string after use */
|
|
||||||
char *url_decode(char *str) {
|
|
||||||
char *pstr = str, *buf = malloc(strlen(str) + 1), *pbuf = buf;
|
|
||||||
while (*pstr) {
|
|
||||||
if (*pstr == '%') {
|
|
||||||
if (pstr[1] && pstr[2]) {
|
|
||||||
*pbuf++ = from_hex(pstr[1]) << 4 | from_hex(pstr[2]);
|
|
||||||
pstr += 2;
|
|
||||||
}
|
|
||||||
} else if (*pstr == '+') {
|
|
||||||
*pbuf++ = ' ';
|
|
||||||
} else {
|
|
||||||
*pbuf++ = *pstr;
|
|
||||||
}
|
|
||||||
pstr++;
|
|
||||||
}
|
|
||||||
*pbuf = '\0';
|
|
||||||
return buf;
|
|
||||||
}
|
|
170
src/ympd.c
170
src/ympd.c
@ -26,39 +26,20 @@
|
|||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <libwebsockets.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#include "mongoose.h"
|
||||||
#include "http_server.h"
|
#include "http_server.h"
|
||||||
#include "mpd_client.h"
|
#include "mpd_client.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
extern char *optarg;
|
extern char *optarg;
|
||||||
extern char *resource_path;
|
|
||||||
|
|
||||||
struct libwebsocket_protocols protocols[] = {
|
|
||||||
/* first protocol must always be HTTP handler */
|
|
||||||
{
|
|
||||||
"http-only", /* name */
|
|
||||||
callback_http, /* callback */
|
|
||||||
sizeof (struct per_session_data__http), /* per_session_data_size */
|
|
||||||
0, /* max frame size / rx buffer */
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ympd-client",
|
|
||||||
callback_ympd,
|
|
||||||
sizeof(struct per_session_data__ympd),
|
|
||||||
255,
|
|
||||||
0,
|
|
||||||
},
|
|
||||||
|
|
||||||
{ NULL, NULL, 0, 0, 0 } /* terminator */
|
|
||||||
};
|
|
||||||
|
|
||||||
int force_exit = 0;
|
int force_exit = 0;
|
||||||
|
|
||||||
@ -67,80 +48,62 @@ void bye()
|
|||||||
force_exit = 1;
|
force_exit = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int server_callback(struct mg_connection *c) {
|
||||||
|
|
||||||
|
//printf("Got REQ: (%lu) WS:%d\n", c->content_len, c->is_websocket);
|
||||||
|
//fwrite(c->content, c->content_len, 1, stdout);
|
||||||
|
//printf("\n");
|
||||||
|
|
||||||
|
if (c->is_websocket)
|
||||||
|
{
|
||||||
|
c->content[c->content_len] = '\0';
|
||||||
|
if(c->content_len)
|
||||||
|
return callback_mpd(c);
|
||||||
|
else
|
||||||
|
return MG_CLIENT_CONTINUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return callback_http(c);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
int n, gid = -1, uid = -1;
|
int n, option_index = 0;
|
||||||
int option_index = 0;
|
struct mg_server *server = mg_create_server(NULL);
|
||||||
unsigned int oldus = 0;
|
unsigned int current_timer = 0, last_timer = 0;
|
||||||
struct libwebsocket_context *context;
|
|
||||||
struct lws_context_creation_info info;
|
|
||||||
const char *cert_filepath = NULL;
|
|
||||||
const char *private_key_filepath = NULL;
|
|
||||||
const char *iface = NULL;
|
|
||||||
|
|
||||||
atexit(bye);
|
atexit(bye);
|
||||||
memset(&info, 0, sizeof info);
|
mg_set_option(server, "listening_port", "8080");
|
||||||
info.port = 8080;
|
mpd.port = 6600;
|
||||||
strcpy(mpd_host, "127.0.0.1");
|
strcpy(mpd.host, "127.0.0.1");
|
||||||
mpd_port = 6600;
|
|
||||||
lws_set_log_level(LLL_ERR | LLL_WARN, NULL);
|
|
||||||
|
|
||||||
static struct option long_options[] = {
|
static struct option long_options[] = {
|
||||||
{"host", required_argument, 0, 'h'},
|
{"host", required_argument, 0, 'h'},
|
||||||
{"port", required_argument, 0, 'p'},
|
{"port", required_argument, 0, 'p'},
|
||||||
{"interface", required_argument, 0, 'i'},
|
|
||||||
{"webport", required_argument, 0, 'w'},
|
{"webport", required_argument, 0, 'w'},
|
||||||
{"resourcepath", required_argument, 0, 'r'},
|
{"user", required_argument, 0, 'u'},
|
||||||
{"ssl_cert", required_argument, 0, 'c'},
|
|
||||||
{"ssl_key", required_argument, 0, 'k'},
|
|
||||||
{"gid", required_argument, 0, 'g'},
|
|
||||||
{"uid", required_argument, 0, 'u'},
|
|
||||||
{"verbose", optional_argument, 0, 'v'},
|
|
||||||
{"version", no_argument, 0, 'V'},
|
{"version", no_argument, 0, 'V'},
|
||||||
{"help", no_argument, 0, 0 },
|
{"help", no_argument, 0, 0 },
|
||||||
{0, 0, 0, 0 }
|
{0, 0, 0, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
while((n = getopt_long(argc, argv, "h:p:i:w:r:c:k:g:u:v::V",
|
while((n = getopt_long(argc, argv, "h:p:w:u::V",
|
||||||
long_options, &option_index)) != -1) {
|
long_options, &option_index)) != -1) {
|
||||||
switch (n) {
|
switch (n) {
|
||||||
case 'h':
|
case 'h':
|
||||||
strncpy(mpd_host, optarg, sizeof(mpd_host));
|
strncpy(mpd.host, optarg, sizeof(mpd.host));
|
||||||
break;
|
break;
|
||||||
case 'p':
|
case 'p':
|
||||||
mpd_port = atoi(optarg);
|
mpd.port = atoi(optarg);
|
||||||
case 'i':
|
|
||||||
iface = optarg;
|
|
||||||
break;
|
|
||||||
case 'w':
|
case 'w':
|
||||||
info.port = atoi(optarg);
|
mg_set_option(server, "listening_port", optarg);
|
||||||
break;
|
|
||||||
case 'r':
|
|
||||||
resource_path = optarg;
|
|
||||||
break;
|
|
||||||
case 'c':
|
|
||||||
cert_filepath = optarg;
|
|
||||||
break;
|
|
||||||
case 'k':
|
|
||||||
private_key_filepath = optarg;
|
|
||||||
break;
|
|
||||||
case 'g':
|
|
||||||
gid = atoi(optarg);
|
|
||||||
break;
|
break;
|
||||||
case 'u':
|
case 'u':
|
||||||
uid = atoi(optarg);
|
mg_set_option(server, "run_as_user", optarg);
|
||||||
break;
|
|
||||||
case 'v':
|
|
||||||
if(optarg)
|
|
||||||
lws_set_log_level(strtol(optarg, NULL, 10), NULL);
|
|
||||||
else
|
|
||||||
lws_set_log_level(LLL_ERR | LLL_WARN |
|
|
||||||
LLL_NOTICE | LLL_INFO, NULL);
|
|
||||||
break;
|
break;
|
||||||
case 'V':
|
case 'V':
|
||||||
fprintf(stdout, "ympd %d.%d.%d\n"
|
fprintf(stdout, "ympd %d.%d.%d\n"
|
||||||
"Copyright (C) 2014 Andrew Karpow <andy@ympd.org>\n"
|
"Copyright (C) 2014 Andrew Karpow <andy@ndyk.de>\n"
|
||||||
"Resource Path: "LOCAL_RESOURCE_PATH"\n"
|
|
||||||
"built " __DATE__ " "__TIME__ " ("__VERSION__")\n",
|
"built " __DATE__ " "__TIME__ " ("__VERSION__")\n",
|
||||||
YMPD_VERSION_MAJOR, YMPD_VERSION_MINOR, YMPD_VERSION_PATCH);
|
YMPD_VERSION_MAJOR, YMPD_VERSION_MINOR, YMPD_VERSION_PATCH);
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
@ -149,14 +112,8 @@ int main(int argc, char **argv)
|
|||||||
fprintf(stderr, "Usage: %s [OPTION]...\n\n"
|
fprintf(stderr, "Usage: %s [OPTION]...\n\n"
|
||||||
"\t-h, --host <host>\t\tconnect to mpd at host [localhost]\n"
|
"\t-h, --host <host>\t\tconnect to mpd at host [localhost]\n"
|
||||||
"\t-p, --port <port>\t\tconnect to mpd at port [6600]\n"
|
"\t-p, --port <port>\t\tconnect to mpd at port [6600]\n"
|
||||||
"\t-i, --interface <interface>\tlisten interface for webserver [all]\n"
|
"\t-w, --webport [ip:]<port>\t\tlisten interface/port for webserver [8080]\n"
|
||||||
"\t-w, --webport <port>\t\tlisten port for webserver [8080]\n"
|
"\t-u, --user <username>\t\t\tdrop priviliges to user after socket bind\n"
|
||||||
"\t-r, --resourcepath <path>\tresourcepath for webserver [" LOCAL_RESOURCE_PATH "]\n"
|
|
||||||
"\t-c, --ssl_cert <filepath>\tssl certificate ssl_private_key_filepath\n"
|
|
||||||
"\t-k, --ssl_key <filepath>\tssl private key filepath\n"
|
|
||||||
"\t-u, --uid <id>\t\t\tuser-id after socket bind\n"
|
|
||||||
"\t-g, --gid <id>\t\t\tgroup-id after socket bind\n"
|
|
||||||
"\t-v, --verbose[<level>]\t\tverbosity level\n"
|
|
||||||
"\t-V, --version\t\t\tget version\n"
|
"\t-V, --version\t\t\tget version\n"
|
||||||
"\t--help\t\t\t\tthis help\n"
|
"\t--help\t\t\t\tthis help\n"
|
||||||
, argv[0]);
|
, argv[0]);
|
||||||
@ -164,52 +121,19 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(cert_filepath != NULL && private_key_filepath == NULL) {
|
mg_set_http_close_handler(server, mpd_close_handler);
|
||||||
lwsl_err("private key filepath needed\n");
|
mg_set_request_handler(server, server_callback);
|
||||||
return EXIT_FAILURE;
|
while (!force_exit) {
|
||||||
}
|
current_timer = mg_poll_server(server, 200);
|
||||||
|
if(current_timer - last_timer)
|
||||||
if(private_key_filepath != NULL && cert_filepath == NULL) {
|
{
|
||||||
lwsl_err("public cert filepath needed\n");
|
last_timer = current_timer;
|
||||||
return EXIT_FAILURE;
|
mpd_poll(server);
|
||||||
}
|
|
||||||
|
|
||||||
info.ssl_cert_filepath = cert_filepath;
|
|
||||||
info.ssl_private_key_filepath = private_key_filepath;
|
|
||||||
info.iface = iface;
|
|
||||||
info.protocols = protocols;
|
|
||||||
info.extensions = libwebsocket_get_internal_extensions();
|
|
||||||
info.gid = gid;
|
|
||||||
info.uid = uid;
|
|
||||||
info.options = 0;
|
|
||||||
|
|
||||||
context = libwebsocket_create_context(&info);
|
|
||||||
if (context == NULL) {
|
|
||||||
lwsl_err("libwebsocket init failed\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
n = 0;
|
|
||||||
while (n >= 0 && !force_exit) {
|
|
||||||
struct timeval tv;
|
|
||||||
|
|
||||||
gettimeofday(&tv, NULL);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This provokes the LWS_CALLBACK_SERVER_WRITEABLE for every
|
|
||||||
* live websocket connection using the DUMB_INCREMENT protocol,
|
|
||||||
* as soon as it can take more packets (usually immediately)
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (((unsigned int)tv.tv_usec - oldus) > 1000 * 500) {
|
|
||||||
mpd_loop();
|
|
||||||
libwebsocket_callback_on_writable_all_protocol(&protocols[1]);
|
|
||||||
oldus = tv.tv_usec;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
n = libwebsocket_service(context, 50);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
libwebsocket_context_destroy(context);
|
mpd_disconnect();
|
||||||
return 0;
|
mg_destroy_server(&server);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user