fixup table layout, notifications
authorAndrew Karpow <andy@ndyk.de>
Fri, 17 Jan 2014 15:26:26 +0000 (16:26 +0100)
committerAndrew Karpow <andy@ndyk.de>
Fri, 17 Jan 2014 15:26:26 +0000 (16:26 +0100)
htdocs/css/mpd.css
htdocs/css/slider.css [deleted file]
htdocs/index.html
htdocs/js/bootstrap-notify.js
htdocs/js/mpd.js
src/http_server.c
src/mpd_client.c
src/mpd_client.h
src/ympd_process.c [deleted file]

index ff4d073..ae19e13 100644 (file)
@@ -43,8 +43,6 @@ body {
   border-color: #adadad;
 }
 
-
-
 #salamisandwich td:nth-child(3), th:nth-child(3) {
   text-align: right;
 }
@@ -53,6 +51,10 @@ tbody {
   cursor: pointer;
 }
 
+td:last-child, td:first-child {
+  width: 30px;
+}
+
 .notifications {
   position: fixed;
   z-index: 9999;
diff --git a/htdocs/css/slider.css b/htdocs/css/slider.css
deleted file mode 100644 (file)
index a7f8370..0000000
+++ /dev/null
@@ -1,138 +0,0 @@
-/*!
- * 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: 100%;
-  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;
-}
\ No newline at end of file
index dca5954..bd4778b 100644 (file)
@@ -50,7 +50,7 @@
             <button type="button" class="btn btn-default" onclick="socket.send('MPD_API_SET_STOP');">
               <span id="stop-icon" class="glyphicon glyphicon-stop"></span>
             </button>
-            <button type="button" class="btn btn-default" onclick="socket.send('MPD_API_SET_PAUSE');">
+            <button type="button" class="btn btn-default" onclick="clickPlay();">
               <span id="play-icon" class="glyphicon glyphicon-pause"></span>
             </button>
             <button type="button" class="btn btn-default" onclick="socket.send('MPD_API_SET_PREV');">
                 <th>#</th>
                 <th>Title</th>
                 <th>Duration</th>
+                <th></th>
               </tr>
             </thead>
             <tbody>
index caa3f12..e82fbac 100644 (file)
@@ -46,7 +46,7 @@
         this.$note.html(this.options.message);
 
     if(this.options.closable)
-      var link = $('<a class="close pull-right" href="#">&times;</a>');
+      var link = $('<a class="close pull-right" href="#">&nbsp;&times;</a>');
       $(link).on('click', $.proxy(onClose, this));
       this.$note.prepend(link);
 
index 7dd87b3..d40c96b 100644 (file)
@@ -5,7 +5,6 @@ var current_song = new Object();
 
 var app = $.sammy(function() {
     this.before('/', function(e, data) {
-        socket.send("MPD_API_GET_TRACK_INFO");
         $('#nav_links > li').removeClass('active');
     });
 
@@ -74,8 +73,8 @@ function webSocketConnect() {
     try {
         socket.onopen = function() {
             $('.top-right').notify({
-                message:{text:"Connected"},
-                fadeOut: { enabled: true, delay: 1000 }
+                message:{text:"Connected to ympd"},
+                fadeOut: { enabled: true, delay: 500 }
             }).show();
 
             app.run();
@@ -102,10 +101,10 @@ function webSocketConnect() {
                         var seconds = obj.data[song].duration - minutes * 60;
 
                         $('#salamisandwich > tbody').append(
-                            "<tr trackid=\"" + obj.data[song].id + "\"><td>" + obj.data[song].pos + "</td>" +
+                            "<tr trackid=\"" + obj.data[song].id + "\"><td>" + (obj.data[song].pos + 1) + "</td>" +
                                 "<td>"+ obj.data[song].title +"</td>" + 
                                 "<td>"+ minutes + ":" + (seconds < 10 ? '0' : '') + seconds +
-                        "</td></tr>");
+                        "</td><td></td></tr>");
                     }
 
                     $('#salamisandwich > tbody > tr').on({
@@ -139,7 +138,7 @@ function webSocketConnect() {
                                     "<tr uri=\"" + obj.data[item].dir + "\" class=\"dir\">" +
                                         "<td><span class=\"glyphicon glyphicon-folder-open\"></span></td>" + 
                                         "<td><a>" + basename(obj.data[item].dir) + "</a></td>" + 
-                                "<td></td></tr>");
+                                "<td></td><td></td></tr>");
                             break;
                         case "song":
                             var minutes = Math.floor(obj.data[item].duration / 60);
@@ -149,7 +148,7 @@ function webSocketConnect() {
                                 "<tr uri=\"" + obj.data[item].uri + "\" class=\"song\">" +
                                     "<td><span class=\"glyphicon glyphicon-music\"></span></td>" + 
                                     "<td>" + obj.data[item].title +"</td>" + 
-                                    "<td>"+ minutes + ":" + (seconds < 10 ? '0' : '') + seconds +"</td></tr>");
+                                    "<td>"+ minutes + ":" + (seconds < 10 ? '0' : '') + seconds +"</td><td></td></tr>");
                                 break;
 
                             case "playlist":
@@ -157,29 +156,33 @@ function webSocketConnect() {
                         }
                     }
 
+                    function appendClickableIcon(appendTo, onClickAction, glyphicon) {
+                        $(appendTo).children().last().append(
+                            "<a role=\"button\" class=\"pull-right btn-group-hover\">" +
+                            "<span class=\"glyphicon glyphicon-" + glyphicon + "\"></span></a>")
+                            .find('a').click(function(e) {
+                                e.stopPropagation();
+                                socket.send(onClickAction + "," + $(this).parents("tr").attr("uri"));
+                            $('.top-right').notify({
+                                message:{
+                                    text: $('td:nth-child(2)', $(this).parents("tr")).text() + " added"
+                                } }).show();
+                            }).fadeTo('fast',1);
+                    }
+
                     $('#salamisandwich > tbody > tr').on({
-                        mouseenter: function(){
-                            if($(this).is(".dir")) {
-                                    $(this).children().last().append(
-                                        "<a role=\"button\" class=\"pull-right btn-group-hover\">" +
-                                        "Add Directory <span class=\"glyphicon glyphicon-plus\"></span></a>")
-                                .find('a').click(function(e) {
-                                    e.stopPropagation();
-                                    socket.send("MPD_API_ADD_TRACK," + $(this).parents("tr").attr("uri"));
-                                    $('.top-right').notify({
-                                        message:{
-                                            text:"Added " + $('td:nth-child(2)', $(this).parents("tr")).text() + " to playlist "
-                                        }
-                                    }).show();
-                                }).fadeTo('fast',1);
-                            }
+                        mouseenter: function() {
+                            if($(this).is(".dir")) 
+                                appendClickableIcon($(this), 'MPD_API_ADD_TRACK', 'plus');
+                            else if($(this).is(".song"))
+                                appendClickableIcon($(this), 'MPD_API_ADD_PLAY_TRACK', 'play');
                         },
                         click: function() {
                             if($(this).is(".song")) {
                                 socket.send("MPD_API_ADD_TRACK," + $(this).attr("uri"));
                                 $('.top-right').notify({
                                     message:{
-                                        text:"Added " + $('td:nth-child(2)', this).text() + " to playlist "
+                                        text: $('td:nth-child(2)', this).text() + " added"
                                     }
                                 }).show();
 
@@ -239,9 +242,6 @@ function webSocketConnect() {
                     else
                         $('#btnrepeat').removeClass("active");
 
-                    if(obj.data.elapsedTime <= 1)
-                        socket.send("MPD_API_GET_TRACK_INFO");
-
                     last_state = obj;
                     break;
                 case "disconnected":
@@ -256,12 +256,23 @@ function webSocketConnect() {
                     if(current_app === 'playlist')
                         $.get( "/api/get_playlist", socket.onmessage);
                     break;
-                case "current_song":
+                case "song_change":
                     $('#currenttrack').text(" " + obj.data.title);
-                    if(obj.data.album)
+                    var notification = "<strong><h4>" + obj.data.title + "</h4></strong>";
+
+                    if(obj.data.album) {
                         $('#album').text(obj.data.album);
-                    if(obj.data.artist)
+                        notification += obj.data.album + "<br />";
+                    }
+                    if(obj.data.artist) {
                         $('#artist').text(obj.data.artist);
+                        notification += obj.data.artist + "<br />";
+                    }
+
+                    $('.top-right').notify({
+                        message:{html: notification},
+                        type: "info",
+                    }).show();
                     break;
                 case "error":
                     $('.top-right').notify({
@@ -350,14 +361,20 @@ var updatePlayIcon = function(state)
     }
 }
 
-function updateDB()
-{
+function updateDB() {
     socket.send('MPD_API_UPDATE_DB');
     $('.top-right').notify({
         message:{text:"Updating MPD Database... "}
     }).show();
 }
 
+function clickPlay() {
+    if($('#track-icon').hasClass('glyphicon-stop'))
+        socket.send('MPD_API_SET_PLAY');
+    else
+        socket.send('MPD_API_SET_PAUSE');
+}
+
 function basename(path) {
     return path.split('/').reverse()[0];
 }
index 511f7c9..15e8bbf 100644 (file)
@@ -20,7 +20,6 @@ struct serveable {
 
 static const struct serveable whitelist[] = {
     { "/css/bootstrap.css", "text/css" },
-    { "/css/slider.css", "text/css" },
     { "/css/mpd.css", "text/css" },
 
     { "/js/bootstrap.min.js", "text/javascript" },
index 6ceb56a..c38f74f 100644 (file)
@@ -16,7 +16,6 @@
 struct mpd_connection *conn = NULL;
 enum mpd_conn_states mpd_conn_state = MPD_DISCONNECTED;
 enum mpd_state mpd_play_state = MPD_STATE_UNKNOWN;
-unsigned queue_version;
 
 int callback_ympd(struct libwebsocket_context *context,
         struct libwebsocket *wsi,
@@ -53,10 +52,9 @@ int callback_ympd(struct libwebsocket_context *context,
             else if(mpd_conn_state != MPD_CONNECTED) {
                 n = snprintf(p, MAX_SIZE, "{\"type\":\"disconnected\"}");
             }
-            else if((pss->queue_version != queue_version) || (pss->do_send & DO_SEND_PLAYLIST)) {
+            else if(pss->do_send & DO_SEND_PLAYLIST) {
                 /*n = mpd_put_playlist(p);*/
                 n = snprintf(p, MAX_SIZE, "{\"type\":\"update_playlist\"}");
-                pss->queue_version = queue_version;
                 pss->do_send &= ~DO_SEND_PLAYLIST;
             }
             else if(pss->do_send & DO_SEND_TRACK_INFO) {
@@ -68,8 +66,24 @@ int callback_ympd(struct libwebsocket_context *context,
                 pss->do_send &= ~DO_SEND_BROWSE;
                 free(pss->browse_path);
             }
-            else
-                n = mpd_put_state(p);
+            else {
+                /* Default Action */
+                int current_song_id;
+                unsigned queue_version;
+
+                n = mpd_put_state(p, &current_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);
@@ -105,6 +119,8 @@ int callback_ympd(struct libwebsocket_context *context,
                 unsigned id;
                 if(sscanf(in, "MPD_API_RM_TRACK,%u", &id))
                     mpd_run_delete_id(conn, id);
+
+                libwebsocket_callback_on_writable(context, wsi);
             }
             else if(!strncmp((const char *)in, MPD_API_PLAY_TRACK, sizeof(MPD_API_PLAY_TRACK)-1)) {
                 unsigned id;
@@ -156,6 +172,16 @@ int callback_ympd(struct libwebsocket_context *context,
                     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);
+                }
+            }
+            
 
             if(mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS)
                 pss->do_send |= DO_SEND_ERROR;
@@ -226,7 +252,7 @@ char* mpd_get_title(struct mpd_song const *song)
     return basename(str);
 }
 
-int mpd_put_state(char *buffer)
+int mpd_put_state(char *buffer, int *current_song_id, unsigned *queue_version)
 {
     struct mpd_status *status;
     int len;
@@ -256,7 +282,8 @@ int mpd_put_state(char *buffer)
             mpd_status_get_total_time(status),
             mpd_status_get_song_id(status));
 
-    queue_version = mpd_status_get_queue_version(status);
+    *current_song_id = mpd_status_get_song_id(status);
+    *queue_version = mpd_status_get_queue_version(status);
     mpd_status_free(status);
     return len;
 }
@@ -271,7 +298,7 @@ int mpd_put_current_song(char *buffer)
     if(song == NULL)
         return 0;
 
-    cur += snprintf(cur, end - cur, "{\"type\": \"current_song\", \"data\":"
+    cur += snprintf(cur, end - cur, "{\"type\": \"song_change\", \"data\":"
             "{\"pos\":%d, \"title\":\"%s\"",
             mpd_song_get_pos(song),
             mpd_get_title(song));
index 287ecc1..ab6c422 100644 (file)
@@ -17,6 +17,7 @@
 #define MPD_API_GET_TRACK_INFO   "MPD_API_GET_TRACK_INFO"
 #define MPD_API_GET_BROWSE       "MPD_API_GET_BROWSE"
 #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"
@@ -36,6 +37,7 @@
 struct per_session_data__ympd {
     int do_send;
     unsigned queue_version;
+    int current_song_id;
     char *browse_path;
 };
 
@@ -51,7 +53,7 @@ int callback_ympd(struct libwebsocket_context *context,
         enum libwebsocket_callback_reasons reason,
         void *user, void *in, size_t len);
 void mpd_loop();
-int mpd_put_state(char *buffer);
+int mpd_put_state(char *buffer, int *current_song_id, unsigned *queue_version);
 int mpd_put_current_song(char *buffer);
 int mpd_put_playlist(char *buffer);
 int mpd_put_browse(char *buffer, char *path);
diff --git a/src/ympd_process.c b/src/ympd_process.c
deleted file mode 100644 (file)
index 40ab565..0000000
+++ /dev/null
@@ -1 +0,0 @@
-ympd_process.c
\ No newline at end of file