diff --git a/.travis.yml b/.travis.yml index aab29ee..4b2afb9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: c sudo: required -dist: precise +dist: trusty compiler: - gcc diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d0e1a0..58ef158 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,8 +24,8 @@ include_directories(${PROJECT_BINARY_DIR} ${PROJECT_SOURCE_DIR} ${LIBMPDCLIENT_I include(CheckCSourceCompiles) -set(CMAKE_C_FLAGS "-std=gnu99 -Wall") -set(CMAKE_C_FLAGS_DEBUG "-ggdb -pedantic") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wall") +set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -ggdb -pedantic") if(WITH_IPV6) set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS NS_ENABLE_IPV6) endif() diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c89b804 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,13 @@ +FROM alpine:3.5 +WORKDIR /app/build +COPY . /app +RUN apk add --no-cache g++ make cmake libmpdclient-dev openssl-dev +RUN cmake .. +RUN make + +FROM alpine:3.5 +RUN apk add --no-cache libmpdclient openssl +EXPOSE 8080 +COPY --from=0 /app/build/ympd /usr/bin/ympd +COPY --from=0 /app/build/mkdata /usr/bin/mkdata +CMD ympd \ No newline at end of file diff --git a/README.md b/README.md index af3a310..4bf91f2 100644 --- a/README.md +++ b/README.md @@ -13,11 +13,12 @@ Dependencies ------------ - libmpdclient 2: http://www.musicpd.org/libs/libmpdclient/ - cmake 2.6: http://cmake.org/ + - OpenSSL: https://www.openssl.org/ Unix Build Instructions ----------------------- -1. install dependencies, cmake and libmpdclient are available from all major distributions. +1. install dependencies. cmake, libmpdclient (dev), and OpenSSL (dev) are available from all major distributions. 2. create build directory ```cd /path/to/src; mkdir build; cd build``` 3. create makefile ```cmake .. -DCMAKE_INSTALL_PREFIX:PATH=/usr``` 4. build ```make``` @@ -28,14 +29,15 @@ Run flags ``` Usage: ./ympd [OPTION]... - -d, --digest path to htdigest file for authorization - (realm ympd) [no authorization] - -h, --host connect to mpd at host [localhost] - -p, --port connect to mpd at port [6600] - -w, --webport [ip:] listen interface/port for webserver [8080] - -u, --user drop priviliges to user after socket bind - -V, --version get version - --help this help + -D, --digest path to htdigest file for authorization + (realm ympd) [no authorization] + -h, --host connect to mpd at host [localhost] + -p, --port connect to mpd at port [6600] + -w, --webport [ip:] listen interface/port for webserver [8080] + -d, --dirbletoken Dirble API token + -u, --user drop priviliges to user after socket bind + -V, --version get version + --help this help ``` SSL Support diff --git a/contrib/init.debian b/contrib/init.debian index e1c6fab..012adf1 100755 --- a/contrib/init.debian +++ b/contrib/init.debian @@ -24,6 +24,7 @@ YMPD_USER=nobody MPD_HOST=localhost MPD_PORT=6600 WEB_PORT=8080 +DIRBLE_API_TOKEN=2e223c9909593b94fc6577361a #DIGEST=--digest /path/to/htdigest @@ -36,7 +37,7 @@ WEB_PORT=8080 # Load the VERBOSE setting and other rcS variables [ -f /etc/default/rcS ] && . /etc/default/rcS -DAEMON_OPT="--user $YMPD_USER --webport $WEB_PORT --host $MPD_HOST --port $MPD_PORT $DIGEST" +DAEMON_OPT="--user $YMPD_USER --mpdpass '$MPD_PASSWORD' --webport $WEB_PORT --host $MPD_HOST --port $MPD_PORT --dirbletoken $DIRBLE_API_TOKEN $DIGEST" do_start() { diff --git a/contrib/ympd.default b/contrib/ympd.default index 184b2ed..27e9ebb 100644 --- a/contrib/ympd.default +++ b/contrib/ympd.default @@ -1,5 +1,7 @@ MPD_HOST=localhost MPD_PORT=6600 +MPD_PASSWORD= WEB_PORT=8080 YMPD_USER=nobody +DIRBLE_API_TOKEN=2e223c9909593b94fc6577361a #DIGEST=--digest /path/to/htdigest diff --git a/contrib/ympd.freebsd b/contrib/ympd.freebsd new file mode 100755 index 0000000..4dcbedd --- /dev/null +++ b/contrib/ympd.freebsd @@ -0,0 +1,29 @@ +#!/bin/sh + +# PROVIDE: ympd +# REQUIRE: DAEMON musicpd +# KEYWORD: shutdown + +# Add the following line to /etc/rc.conf to enable ympd: +# +# ympd_enable="YES" + +. /etc/rc.subr + +name="ympd" +rcvar="${name}_enable" +command="/usr/local/bin/ympd" +pidfile="/var/run/${name}.pid" +start_cmd="ympd_start" + +load_rc_config "$name" +: ${ympd_enable:="NO"} +: ${ympd_user:="nobody"} + +ympd_start() +{ + check_startmsgs && echo "Starting ${name}." + /usr/sbin/daemon -f -p "${pidfile}" -t "${name}" -u "${ympd_user}" "${command}" +} + +run_rc_command "$1" diff --git a/contrib/ympd.service b/contrib/ympd.service index 4969337..4151534 100644 --- a/contrib/ympd.service +++ b/contrib/ympd.service @@ -5,11 +5,13 @@ Requires=network.target local-fs.target [Service] Environment=MPD_HOST=localhost Environment=MPD_PORT=6600 +Environment=MPD_PASSWORD= Environment=WEB_PORT=8080 Environment=YMPD_USER=nobody +Environment=DIRBLE_API_TOKEN=2e223c9909593b94fc6577361a Environment=DIGEST= EnvironmentFile=/etc/default/ympd -ExecStart=/usr/bin/ympd --user $YMPD_USER --webport $WEB_PORT --host $MPD_HOST --port $MPD_PORT $DIGEST +ExecStart=/usr/bin/ympd --user $YMPD_USER --mpdpass "$MPD_PASSWORD" --webport $WEB_PORT --host $MPD_HOST --port $MPD_PORT --dirbletoken $DIRBLE_API_TOKE $DIGEST Type=simple [Install] diff --git a/htdocs/index.html b/htdocs/index.html index 083c11f..2ce9359 100644 --- a/htdocs/index.html +++ b/htdocs/index.html @@ -116,7 +116,7 @@ -
+
diff --git a/htdocs/js/mpd.js b/htdocs/js/mpd.js index b6e7644..09b7705 100644 --- a/htdocs/js/mpd.js +++ b/htdocs/js/mpd.js @@ -1,6 +1,6 @@ /* ympd (c) 2013-2014 Andrew Karpow - This project's homepage is: http://www.ympd.org + This project's homepage is: https://www.ympd.org This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -31,6 +31,9 @@ var dirble_selected_cat = ""; var dirble_catid = ""; var dirble_page = 1; var isTouch = Modernizr.touch ? 1 : 0; +var filter = undefined; +var dirble_api_token = ""; +var dirble_stations = false; var app = $.sammy(function() { @@ -38,6 +41,7 @@ var app = $.sammy(function() { current_app = 'queue'; $('#breadcrump').addClass('hide'); + $('#filter').addClass('hide'); $('#salamisandwich').removeClass('hide').find("tr:gt(0)").remove(); $('#dirble_panel').addClass('hide'); socket.send('MPD_API_GET_QUEUE,'+pagination); @@ -65,7 +69,8 @@ var app = $.sammy(function() { browsepath = this.params['splat'][1]; pagination = parseInt(this.params['splat'][0]); current_app = 'browse'; - $('#breadcrump').removeClass('hide').empty().append("
  • root
  • "); + $('#breadcrump').removeClass('hide').empty().append("
  • root
  • "); + $('#filter').removeClass('hide'); $('#salamisandwich').removeClass('hide').find("tr:gt(0)").remove(); $('#dirble_panel').addClass('hide'); socket.send('MPD_API_GET_BROWSE,'+pagination+','+(browsepath ? browsepath : "/")); @@ -132,7 +137,13 @@ var app = $.sammy(function() { dirble_catid = this.params['splat'][0]; dirble_page = this.params['splat'][1]; - dirble_load_stations(); + dirble_stations = true; + + if(dirble_api_token) { + dirble_load_stations(); + } else { + getDirbleApiToken(); + } }); @@ -152,7 +163,13 @@ var app = $.sammy(function() { $('#panel-heading').text("Dirble"); $('#dirble').addClass('active'); - dirble_load_categories(); + dirble_stations = false; + + if(dirble_api_token) { + dirble_load_categories(); + } else { + getDirbleApiToken(); + } }); this.get("/", function(context) { @@ -186,6 +203,8 @@ $(document).ready(function(){ else if ($.cookie("notification") === "true") $('#btnnotify').addClass("active") + + add_filter(); document.getElementById('player').addEventListener('stalled', function() { if ( !document.getElementById('player').paused ) { @@ -373,20 +392,33 @@ function webSocketConnect() { for (var item in obj.data) { switch(obj.data[item].type) { case "directory": + var clazz = 'dir'; + if (filter !== undefined) { + var first = obj.data[item].dir[0]; + if (filter === "#" && isNaN(first)) { + clazz += ' hide'; + } else if (filter >= "A" && filter <= "Z" && first.toUpperCase() !== filter) { + clazz += ' hide'; + } else if (filter === "||") { + clazz += ' hide'; + } + } $('#salamisandwich > tbody').append( - "" + - "" + - "" + basename(obj.data[item].dir) + "" + - "" + + "" + + "" + + "" + basename(obj.data[item].dir) + "" + "" ); break; case "playlist": + var clazz = 'plist'; + if (filter !== "||") { + clazz += ' hide'; + } $('#salamisandwich > tbody').append( - "" + - "" + - "" + basename(obj.data[item].plist) + "" + - "" + + "" + + "" + + "" + basename(obj.data[item].plist) + "" + "" ); break; @@ -603,6 +635,15 @@ function webSocketConnect() { if(obj.data.passwort_set) $('#mpd_password_set').removeClass('hide'); break; + case "dirbleapitoken": + dirble_api_token = obj.data; + + if(dirble_stations) { + dirble_load_stations(); + } else { + dirble_load_categories(); + } + break; case "error": $('.top-right').notify({ message:{text: obj.data}, @@ -713,16 +754,6 @@ function clickPlay() { socket.send('MPD_API_SET_PAUSE'); } -function renumber_table(tableID,item) { - was = item.children("td").first().text();//Check if first item exists! - is = item.index() + 1;//maybe add pagination - - if (was != is) { - socket.send("MPD_API_MOVE_TRACK," + was + "," + is); - socket.send('MPD_API_GET_QUEUE,'+pagination); - } -} - function clickLocalPlay() { var player = document.getElementById('player'); $("#localplay-icon").removeClass("glyphicon-play").removeClass("glyphicon-pause"); @@ -782,6 +813,16 @@ function trash(tr) { }; } +function renumber_table(tableID,item) { + was = item.children("td").first().text();//Check if first item exists! + is = item.index() + 1;//maybe add pagination + + if (was != is) { + socket.send("MPD_API_MOVE_TRACK," + was + "," + is); + socket.send('MPD_API_GET_QUEUE,'+pagination); + } +} + function basename(path) { return path.split('/').reverse()[0]; } @@ -854,6 +895,10 @@ function getHost() { $('#mpd_pw_con').keypress(onEnter); } +function getDirbleApiToken() { + socket.send('MPD_API_GET_DIRBLEAPITOKEN'); +} + $('#search').submit(function () { app.setLocation("#/search/"+$('#search > div > input').val()); $('#wait').modal('show'); @@ -987,7 +1032,7 @@ function dirble_load_categories() { dirble_page = 1; - $.getJSON( "http://api.dirble.com/v2/categories?token="+TOKEN, function( data ) { + $.getJSON( "https://api.dirble.com/v2/categories?token=" + dirble_api_token, function( data ) { $('#dirble_loading').addClass('hide'); @@ -1035,7 +1080,7 @@ function dirble_load_categories() { function dirble_load_stations() { - $.getJSON( "http://api.dirble.com/v2/category/"+dirble_catid+"/stations?page="+dirble_page+"&per_page=20&token="+TOKEN, function( data ) { + $.getJSON( "https://api.dirble.com/v2/category/"+dirble_catid+"/stations?page="+dirble_page+"&per_page=20&token=" + dirble_api_token, function( data ) { $('#dirble_loading').addClass('hide'); if (data.length == 20) $('#next').removeClass('hide'); @@ -1062,7 +1107,7 @@ function dirble_load_stations() { click: function() { var _this = $(this); - $.getJSON( "http://api.dirble.com/v2/station/"+$(this).attr("radioid")+"?token="+TOKEN, function( data ) { + $.getJSON( "https://api.dirble.com/v2/station/"+$(this).attr("radioid")+"?token=" + dirble_api_token, function( data ) { socket.send("MPD_API_ADD_TRACK," + data.streams[0].stream); $('.top-right').notify({ @@ -1080,7 +1125,7 @@ function dirble_load_stations() { "").find('a').click(function(e) { e.stopPropagation(); - $.getJSON( "http://api.dirble.com/v2/station/"+_this.attr("radioid")+"?token="+TOKEN, function( data ) { + $.getJSON( "https://api.dirble.com/v2/station/"+_this.attr("radioid")+"?token=" + dirble_api_token, function( data ) { socket.send("MPD_API_ADD_PLAY_TRACK," + data.streams[0].stream); $('.top-right').notify({ @@ -1101,7 +1146,7 @@ function dirble_load_stations() { click: function() { var _this = $(this); - $.getJSON( "http://api.dirble.com/v2/station/"+$(this).attr("radioid")+"?token="+TOKEN, function( data ) { + $.getJSON( "https://api.dirble.com/v2/station/"+$(this).attr("radioid")+"?token=" + dirble_api_token, function( data ) { socket.send("MPD_API_ADD_TRACK," + data.streams[0].stream); $('.top-right').notify({ @@ -1119,7 +1164,7 @@ function dirble_load_stations() { "").find('a').click(function(e) { e.stopPropagation(); - $.getJSON( "http://api.dirble.com/v2/station/"+_this.attr("radioid")+"?token="+TOKEN, function( data ) { + $.getJSON( "https://api.dirble.com/v2/station/"+_this.attr("radioid")+"?token=" + dirble_api_token, function( data ) { socket.send("MPD_API_ADD_PLAY_TRACK," + data.streams[0].stream); $('.top-right').notify({ @@ -1137,3 +1182,56 @@ function dirble_load_stations() { }); }); } + +function set_filter (c) { + filter = c; + + $.each($('#salamisandwich > tbody > tr.dir'), function(i, line) { + var first = $(line).attr('uri')[0]; + + if (filter === undefined) { + $(line).removeClass('hide'); + } + + else if (filter === "#") { + if (!isNaN(first)) { + $(line).removeClass('hide'); + } else { + $(line).addClass('hide'); + } + } + + else if (filter >= "A" && filter <= "Z") { + if (first.toUpperCase() === filter) { + $(line).removeClass('hide'); + } else { + $(line).addClass('hide'); + } + } + + else if (filter === "||") { + $(line).addClass('hide'); + } + }); + + $.each($('#salamisandwich > tbody > tr.plist'), function(i, line) { + if (filter === undefined) { + $(line).removeClass('hide'); + } else if (filter === "||") { + $(line).removeClass('hide'); + } else { + $(line).addClass('hide'); + } + }); +} + +function add_filter () { + $('#filter').append(' #'); + + for (i = 65; i <= 90; i++) { + var c = String.fromCharCode(i); + $('#filter').append(' ' + c + ''); + } + + $('#filter').append(' '); +} diff --git a/src/mpd_client.c b/src/mpd_client.c index 701cc5f..80f15e1 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -64,7 +64,8 @@ int callback_mpd(struct mg_connection *c) return MG_TRUE; if(mpd.conn_state != MPD_CONNECTED && cmd_id != MPD_API_SET_MPDHOST && - cmd_id != MPD_API_GET_MPDHOST && cmd_id != MPD_API_SET_MPDPASS) + cmd_id != MPD_API_GET_MPDHOST && cmd_id != MPD_API_SET_MPDPASS && + cmd_id != MPD_API_GET_DIRBLEAPITOKEN) return MG_TRUE; switch(cmd_id) @@ -290,6 +291,10 @@ out_host_change: "{\"host\" : \"%s\", \"port\": \"%d\", \"passwort_set\": %s}" "}", mpd.host, mpd.port, mpd.password ? "true" : "false"); break; + case MPD_API_GET_DIRBLEAPITOKEN: + n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"dirbleapitoken\", \"" + "data\": \"%s\"}", dirble_api_token); + break; case MPD_API_SET_MPDPASS: p_charbuf = strdup(c->content); if(strcmp(strtok(p_charbuf, ","), "MPD_API_SET_MPDPASS")) diff --git a/src/mpd_client.h b/src/mpd_client.h index 951869d..447dd56 100644 --- a/src/mpd_client.h +++ b/src/mpd_client.h @@ -40,6 +40,7 @@ X(MPD_API_GET_QUEUE) \ X(MPD_API_GET_BROWSE) \ X(MPD_API_GET_MPDHOST) \ + X(MPD_API_GET_DIRBLEAPITOKEN) \ X(MPD_API_ADD_TRACK) \ X(MPD_API_ADD_PLAY_TRACK) \ X(MPD_API_ADD_PLAYLIST) \ @@ -97,6 +98,8 @@ struct t_mpd { unsigned queue_version; } mpd; +char dirble_api_token[28]; + struct t_mpd_client_session { int song_id; unsigned queue_version; diff --git a/src/ympd.c b/src/ympd.c index d8b1b65..1252cb5 100644 --- a/src/ympd.c +++ b/src/ympd.c @@ -95,21 +95,25 @@ int main(int argc, char **argv) mpd.port = 6600; strcpy(mpd.host, "127.0.0.1"); + strcpy(dirble_api_token, "2e223c9909593b94fc6577361a"); + static struct option long_options[] = { - {"digest", required_argument, 0, 'd'}, + {"digest", required_argument, 0, 'D'}, {"host", required_argument, 0, 'h'}, {"port", required_argument, 0, 'p'}, {"webport", required_argument, 0, 'w'}, + {"dirbletoken", required_argument, 0, 'd'}, {"user", required_argument, 0, 'u'}, {"version", no_argument, 0, 'v'}, {"help", no_argument, 0, 0 }, + {"mpdpass", required_argument, 0, 'm'}, {0, 0, 0, 0 } }; - while((n = getopt_long(argc, argv, "d:h:p:w:u:v", + while((n = getopt_long(argc, argv, "D:h:p:w:u:d:v:m", long_options, &option_index)) != -1) { switch (n) { - case 'd': + case 'D': gpass = strdup(optarg); break; case 'h': @@ -121,9 +125,15 @@ int main(int argc, char **argv) case 'w': webport = strdup(optarg); break; + case 'd': + strncpy(dirble_api_token, optarg, sizeof(dirble_api_token)); + break; case 'u': run_as_user = strdup(optarg); break; + case 'm': + mpd.password = strdup(optarg); + break; case 'v': fprintf(stdout, "ympd %d.%d.%d\n" "Copyright (C) 2014 Andrew Karpow \n" @@ -133,13 +143,15 @@ int main(int argc, char **argv) break; default: fprintf(stderr, "Usage: %s [OPTION]...\n\n" - " -d, --digest \tpath to htdigest file for authorization\n" + " -D, --digest \tpath to htdigest file for authorization\n" " \t(realm ympd) [no authorization]\n" " -h, --host \t\tconnect to mpd at host [localhost]\n" " -p, --port \t\tconnect to mpd at port [6600]\n" " -w, --webport [ip:]\tlisten interface/port for webserver [8080]\n" " -u, --user \t\tdrop priviliges to user after socket bind\n" + " -d, --dirbletoken \tDirble API token\n" " -v, --version\t\t\tget version\n" + " -m, --mpdpass \tspecifies the password to use when connecting to mpd\n" " --help\t\t\t\tthis help\n" , argv[0]); return EXIT_FAILURE; diff --git a/ympd.1 b/ympd.1 index d94ed30..c8160a6 100644 --- a/ympd.1 +++ b/ympd.1 @@ -23,6 +23,9 @@ specifies the port for the webserver to listen to, defaults to 8080 \fB\-u\fR, \fB\-\-user username\fR drop privileges to the provided username after socket binding .TP +\fB\-m\fR, \fB\-\-mpdpass password\fR +specifies the password to use when connecting to mpd +.TP \fB\-V\fR, \fB\-\-version\fR print version and exit .TP