From 6bb24e72f810b161a5b15bbfd8b5d9c66aceae50 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Tue, 29 May 2018 00:05:56 +0200 Subject: [PATCH 01/23] New layout for browse card First steps for playlist view --- htdocs/css/mpd.css | 2 +- htdocs/index.html | 79 +++++++++++++++--- htdocs/js/mpd.js | 194 ++++++++++++++++++++++++++++++++------------- src/mpd_client.c | 43 ++++++++++ src/mpd_client.h | 4 +- 5 files changed, 256 insertions(+), 66 deletions(-) diff --git a/htdocs/css/mpd.css b/htdocs/css/mpd.css index 4d87f35..7b9c803 100644 --- a/htdocs/css/mpd.css +++ b/htdocs/css/mpd.css @@ -101,7 +101,7 @@ tbody { float: right !important; } -#queue-buttons { +#queue-buttons, #browsePlaylistsButtons { margin-bottom:20px; } diff --git a/htdocs/index.html b/htdocs/index.html index 5d43f8b..7a62a50 100644 --- a/htdocs/index.html +++ b/htdocs/index.html @@ -193,13 +193,74 @@ -
-
Browse
-
+
+ +
+
+ + +
+ + + + + + + + + + + + + + + +
PlaylistLast modified
+
+ +
+
+
+
@@ -211,7 +272,7 @@
- +
@@ -240,7 +301,7 @@
- +
@@ -272,7 +333,7 @@ @@ -381,12 +442,12 @@ " ); } - totalPages=Math.ceil(obj.totalSongs / MAX_ELEMENTS_PER_PAGE); - if (totalPages==0) { totalPages=1; } - $('#queuePaginationTopPage').text('Page '+(pagination / MAX_ELEMENTS_PER_PAGE + 1)+' / '+totalPages); - $('#queuePaginationBottomPage').text('Page '+(pagination / MAX_ELEMENTS_PER_PAGE + 1)+' / '+totalPages); - if (totalPages > 1) { - $('#queuePaginationTopPage').removeClass('disabled'); - $('#queuePaginationBottomPage').removeClass('disabled'); - $('#queuePaginationTopPages').empty(); - $('#queuePaginationBottomPages').empty(); - for (var i=0;i'+(i+1)+''); - $('#queuePaginationBottomPages').append(''); - } - } else { - $('#queuePaginationTopPage').addClass('disabled'); - $('#queuePaginationBottomPage').addClass('disabled'); - } - if(obj.totalSongs > pagination + MAX_ELEMENTS_PER_PAGE) { - $('#queuePaginationTopNext').removeClass('disabled'); - $('#queuePaginationBottomNext').removeClass('disabled'); - } else { - $('#queuePaginationTopNext').addClass('disabled'); - $('#queuePaginationBottomNext').addClass('disabled'); - } - if(pagination > 0) { - $('#queuePaginationTopPrev').removeClass('disabled'); - $('#queuePaginationBottomPrev').removeClass('disabled'); - } else { - $('#queuePaginationTopPrev').addClass('disabled'); - $('#queuePaginationBottomPrev').addClass('disabled'); - } + setPagination(obj.totalSongs); if ( isTouch ) { $('#queueList > tbody > tr > td:last-child').append( @@ -316,10 +317,52 @@ function webSocketConnect() { }, }); break; + case 'playlists': + if(current_app !== 'browsePlaylists') + break; + var nrItems=0; + for (var item in obj.data) { + nrItems++; + var d = new Date(obj.data[item].last_modified * 1000); + $('#'+current_app+'List > tbody').append( + '' + + '' + + '' + + '' + ); + } + setPagination(obj.totalEntities); + if ( isTouch ) { + $('#'+current_app+'List > tbody > tr > td:last-child').append( + "" + + "delete"); + } else { + $('#'+current_app+'List > tbody > tr').on({ + mouseover: function(){ + if($(this).children().last().has("a").length == 0) + $(this).children().last().append( + "" + + "delete"); + }, + mouseleave: function(){ + var doomed = $(this); + $(this).children().last().find("a").stop().remove(); + } + }); + }; + $('#'+current_app+'List > tbody > tr').on({ + click: function() { + socket.send("MPD_API_ADD_PLAYLIST," + decodeURI($(this).attr("uri"))); + showNotification('"' + $('td:nth-last-child(3)', this).text() + '" added','','','success'); + } + }); + break; case 'search': $('#searchList').find("tr:gt(0)").remove(); case 'browse': - if(current_app !== 'browse' && current_app !== 'search') + if(current_app !== 'browseFilesystem' && current_app !== 'search') break; /* The use of encodeURI() below might seem useless, but it's not. It prevents @@ -383,7 +426,7 @@ function webSocketConnect() { ); break; case 'wrap': - if(current_app == 'browse') { + if(current_app == 'browseFilesystem') { $('#browseNext').removeClass('disabled'); $('#browsePagination').removeClass('hide'); } else { @@ -445,10 +488,10 @@ function webSocketConnect() { case 'dir': pagination = 0; browsepath = $(this).attr("uri"); - $("#browse > a").attr("href", '#/browse/'+pagination+'/'+browsepath); - $('#filter > a').attr("href", '#/browse/'+pagination+'/'+browsepath); - app.setLocation('#/browse/'+pagination+'/'+browsepath); - set_filter(''); + $("#browseFilesystemList > a").attr("href", '#/browse/filesystem/'+pagination+'/'+browsepath); + $('#filter > a').attr("href", '#/browse/filesystem/'+pagination+'/'+browsepath); + app.setLocation('#/browse/filesystem/'+pagination+'/'+browsepath); + set_filter(''); break; case 'song': socket.send("MPD_API_ADD_TRACK," + decodeURI($(this).attr("uri"))); @@ -466,9 +509,9 @@ function webSocketConnect() { click: function() { pagination = 0; browsepath = $(this).attr("uri"); - $("#browse > a").attr("href", '#/browse/'+pagination+'/'+browsepath); - $('#filter > a').attr("href", '#/browse/'+pagination+'/'+browsepath); - app.setLocation('#/browse/'+pagination+'/'+browsepath); + $("#browseFilesystemList > a").attr("href", '#/browse/filesystem/'+pagination+'/'+browsepath); + $('#filter > a').attr("href", '#/browse/filesystem/'+pagination+'/'+browsepath); + app.setLocation('#/browse/filesystem/'+pagination+'/'+browsepath); set_filter(''); } }); @@ -671,6 +714,44 @@ function get_appropriate_ws_url() return pcol + u[0] + separator + "ws"; } +function setPagination(number) { + var totalPages=Math.ceil(number / MAX_ELEMENTS_PER_PAGE); + if (totalPages==0) { totalPages=1; } + $('#'+current_app+'PaginationTopPage').text('Page '+(pagination / MAX_ELEMENTS_PER_PAGE + 1)+' / '+totalPages); + $('#'+current_app+'PaginationBottomPage').text('Page '+(pagination / MAX_ELEMENTS_PER_PAGE + 1)+' / '+totalPages); + if (totalPages > 1) { + $('#'+current_app+'PaginationTopPage').removeClass('disabled'); + $('#'+current_app+'PaginationBottomPage').removeClass('disabled'); + $('#'+current_app+'PaginationTopPages').empty(); + $('#'+current_app+'PaginationBottomPages').empty(); + for (var i=0;i'+(i+1)+''); + $('#'+current_app+'PaginationBottomPages').append(''); + } + } else { + $('#'+current_app+'PaginationTopPage').addClass('disabled'); + $('#'+current_app+'PaginationBottomPage').addClass('disabled'); + } + + if(number > pagination + MAX_ELEMENTS_PER_PAGE) { + $('#'+current_app+'PaginationTopNext').removeClass('disabled'); + $('#'+current_app+'PaginationBottomNext').removeClass('disabled'); + $('#'+current_app+'ButtonsBottom').removeClass('hide'); + } else { + $('#'+current_app+'PaginationTopNext').addClass('disabled'); + $('#'+current_app+'PaginationBottomNext').addClass('disabled'); + $('#'+current_app+'ButtonsBottom').addClass('hide'); + } + + if(pagination > 0) { + $('#'+current_app+'PaginationTopPrev').removeClass('disabled'); + $('#'+current_app+'PaginationBottomPrev').removeClass('disabled'); + } else { + $('#'+current_app+'PaginationTopPrev').addClass('disabled'); + $('#'+current_app+'PaginationBottomPrev').addClass('disabled'); + } +} + function updateVolumeIcon(volume) { if (volume == -1) { $('#volumePrct').text('Volumecontrol disabled'); @@ -884,11 +965,8 @@ $('.page-link').on('click', function (e) { } switch(current_app) { - case "queue": - app.setLocation('#/queue/'+pagination); - break; - case "browse": - app.setLocation('#/browse/'+pagination+'/'+browsepath); + case "browseFilesystem": + app.setLocation('#/browse/filesystem/'+pagination+'/'+browsepath); break; } e.preventDefault(); @@ -925,9 +1003,15 @@ function gotoPage(x,element,event) { app.setLocation('#/queue/'+pagination); } break; - case "browse": - app.setLocation('#/browse/'+pagination+'/'+browsepath); + case "browseFilesystem": + app.setLocation('#/browse/filesystem/'+pagination+'/'+browsepath); break; + case "browsePlaylists": + app.setLocation('#/browse/playlists/'+pagination); + break; + case "browseDatabase": + app.setLocation('#/browse/database/'+pagination); + break; } event.preventDefault(); } @@ -1071,15 +1155,15 @@ function set_filter (c) { } function add_filter () { - $('#filter').append('All'); - $('#filter').append('#'); + $('#filter').append('All'); + $('#filter').append('#'); for (i = 65; i <= 90; i++) { var c = String.fromCharCode(i); - $('#filter').append('' + c + ''); + $('#filter').append('' + c + ''); } - $('#filter').append('list'); + $('#filter').append('list'); } function chVolume (increment) { diff --git a/src/mpd_client.c b/src/mpd_client.c index 52cc596..a7cab64 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -170,6 +170,10 @@ int callback_mpd(struct mg_connection *c) if(sscanf(c->content, "MPD_API_GET_QUEUE,%u", &uint_buf)) n = mpd_put_queue(mpd.buf, uint_buf); break; + case MPD_API_GET_PLAYLISTS: + if(sscanf(c->content, "MPD_API_GET_PLAYLISTS,%u", &uint_buf)) + n = mpd_put_playlists(mpd.buf, uint_buf); + break; case MPD_API_GET_BROWSE: p_charbuf = strdup(c->content); if(strcmp(strtok(p_charbuf, ","), "MPD_API_GET_BROWSE")) @@ -779,6 +783,45 @@ int mpd_put_browse(char *buffer, char *path, unsigned int offset) return cur - buffer; } +int mpd_put_playlists(char *buffer, unsigned int offset) +{ + char *cur = buffer; + const char *end = buffer + MAX_SIZE; + struct mpd_playlist *pl; + unsigned int entity_count = 0; + + if (!mpd_send_list_playlists(mpd.conn)) + RETURN_ERROR_AND_RECOVER("mpd_send_lists_playlists"); + + cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"playlists\",\"data\":[ "); + + while((pl = mpd_recv_playlist(mpd.conn)) != NULL) { + entity_count++; + if(entity_count > offset && entity_count <= offset+MAX_ELEMENTS_PER_PAGE) { + cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"playlist\",\"plist\":"); + cur += json_emit_quoted_str(cur, end - cur, mpd_playlist_get_path(pl)); + cur += json_emit_raw_str(cur, end - cur, ",\"last_modified\":"); + cur += json_emit_int(cur, end - cur, mpd_playlist_get_last_modified(pl)); + cur += json_emit_raw_str(cur, end - cur, "},"); + } + mpd_playlist_free(pl); + } + + if (mpd_connection_get_error(mpd.conn) != MPD_ERROR_SUCCESS || !mpd_response_finish(mpd.conn)) { + fprintf(stderr, "MPD mpd_send_list_playlists: %s\n", mpd_connection_get_error_message(mpd.conn)); + mpd.conn_state = MPD_FAILURE; + return 0; + } + + /* remove last ',' */ + cur--; + + cur += json_emit_raw_str(cur, end - cur, "],\"totalEntities\":"); + cur += json_emit_int(cur, end - cur, entity_count); + cur += json_emit_raw_str(cur, end - cur, "}"); + return cur - buffer; +} + int mpd_search(char *buffer, char *searchstr) { char *cur = buffer; diff --git a/src/mpd_client.h b/src/mpd_client.h index d0bf7e2..07eea06 100644 --- a/src/mpd_client.h +++ b/src/mpd_client.h @@ -76,7 +76,8 @@ X(MPD_API_SEND_SHUFFLE) \ X(MPD_API_GET_STATS) \ X(MPD_API_SET_MIXRAMPDB) \ - X(MPD_API_SET_MIXRAMPDELAY) + X(MPD_API_SET_MIXRAMPDELAY) \ + X(MPD_API_GET_PLAYLISTS) enum mpd_cmd_ids { MPD_CMDS(GEN_ENUM) @@ -125,6 +126,7 @@ int mpd_put_state(char *buffer, int *current_song_id, int *next_song_id, unsigne int mpd_put_outputs(char *buffer, int putnames); int mpd_put_current_song(char *buffer); int mpd_put_queue(char *buffer, unsigned int offset); +int mpd_put_playlists(char *buffer, unsigned int offset); int mpd_put_browse(char *buffer, char *path, unsigned int offset); int mpd_search(char *buffer, char *searchstr); int mpd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr); From 57f29afccd8bc91cddfaa3a5106b76020f099914 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Tue, 29 May 2018 18:07:06 +0200 Subject: [PATCH 02/23] Playlist view --- htdocs/js/mpd.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/htdocs/js/mpd.js b/htdocs/js/mpd.js index 042e32f..004b97f 100644 --- a/htdocs/js/mpd.js +++ b/htdocs/js/mpd.js @@ -85,6 +85,7 @@ var app = $.sammy(function() { $('#cardBrowse').removeClass('hide'); $('#cardBrowsePlaylists').removeClass('hide'); $('#cardBrowseNavPlaylists').addClass('active'); + $('#browsePlaylistsList').find("tr:gt(0)").remove(); socket.send('MPD_API_GET_PLAYLISTS,'+pagination); }); @@ -335,7 +336,7 @@ function webSocketConnect() { if ( isTouch ) { $('#'+current_app+'List > tbody > tr > td:last-child').append( "" + + "onclick=\"delPlaylist($(this).parents('tr'));\">" + "delete"); } else { $('#'+current_app+'List > tbody > tr').on({ @@ -343,7 +344,7 @@ function webSocketConnect() { if($(this).children().last().has("a").length == 0) $(this).children().last().append( "" + + "onclick=\"delPlaylist($(this).parents('tr'));\">" + "delete"); }, mouseleave: function(){ @@ -358,6 +359,13 @@ function webSocketConnect() { showNotification('"' + $('td:nth-last-child(3)', this).text() + '" added','','','success'); } }); + if (nrItems == 0) { + $('#'+current_app+'List > tbody').append( + "" + + "" + + "" + ); + } break; case 'search': $('#searchList').find("tr:gt(0)").remove(); From 26b4f66fab390a46ec66d1fb70c282e13d489776 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Tue, 29 May 2018 18:27:17 +0200 Subject: [PATCH 03/23] Fixed issue #5 --- htdocs/js/mpd.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/htdocs/js/mpd.js b/htdocs/js/mpd.js index 004b97f..ac6cfba 100644 --- a/htdocs/js/mpd.js +++ b/htdocs/js/mpd.js @@ -279,23 +279,23 @@ function webSocketConnect() { if ( isTouch ) { $('#queueList > tbody > tr > td:last-child').append( - "" + - "delete"); + '' + + 'delete'); } else { $('#queueList > tbody > tr').on({ mouseover: function(){ var doomed = $(this); if ( $('#btntrashmodeup').hasClass('btn-success') ) - doomed = $("#queueList > tbody > tr:lt(" + ($(this).index() + 1) + ")"); + doomed = $('#queueList > tbody > tr:lt(' + ($(this).index() + 1) + ')'); if ( $('#btntrashmodedown').hasClass('btn-success') ) - doomed = $("#queueList > tbody > tr:gt(" + ($(this).index() - 1) + ")"); + doomed = $('#queueList > tbody > tr:gt(' + ($(this).index() - 1) + ')'); $.each(doomed, function(){ if($(this).children().last().has("a").length == 0) $(this).children().last().append( - "" + - "delete") + '' + + 'delete') .find('a').fadeTo('fast',1); }); }, From f122355c3813bae31f8386a57487ca158d012f1d Mon Sep 17 00:00:00 2001 From: jcorporation Date: Tue, 29 May 2018 22:12:11 +0200 Subject: [PATCH 04/23] Search, Queueview and BrowseFilesystem enhancements --- htdocs/css/mpd.css | 10 +- htdocs/index.html | 86 ++++++++++++-- htdocs/js/mpd.js | 282 +++++++++++++++++---------------------------- src/mpd_client.c | 201 ++++++++++++++++---------------- src/mpd_client.h | 5 +- 5 files changed, 292 insertions(+), 292 deletions(-) diff --git a/htdocs/css/mpd.css b/htdocs/css/mpd.css index 7b9c803..acd81ee 100644 --- a/htdocs/css/mpd.css +++ b/htdocs/css/mpd.css @@ -85,14 +85,6 @@ tbody { background-color:#eee; } -#filter > a.active { - font-weight: bold; - pointer-events: none; - cursor: default; - text-decoration: none; - color: black; -} - .hide { display: none !important; } @@ -101,7 +93,7 @@ tbody { float: right !important; } -#queue-buttons, #browsePlaylistsButtons { +#queue-buttons, #browsePlaylistsButtons, #searchButtons, #browseFilesystemButtons, #browseDatabaseButtons { margin-bottom:20px; } diff --git a/htdocs/index.html b/htdocs/index.html index 7a62a50..807b570 100644 --- a/htdocs/index.html +++ b/htdocs/index.html @@ -264,11 +264,24 @@ -
-
+
@@ -293,16 +306,56 @@
list' + basename(obj.data[item].plist) + ''+d.toUTCString()+'
error_outlineNo results
- +
- +
Search + +
+
@@ -325,6 +378,22 @@
+
@@ -332,8 +401,9 @@ diff --git a/htdocs/js/mpd.js b/htdocs/js/mpd.js index ac6cfba..4cac5ef 100644 --- a/htdocs/js/mpd.js +++ b/htdocs/js/mpd.js @@ -32,7 +32,6 @@ var lastSongTitle = ""; var current_song = new Object(); var MAX_ELEMENTS_PER_PAGE = 100; var isTouch = Modernizr.touch ? 1 : 0; -var filter = ""; var playstate = ""; var progressBar; var volumeBar; @@ -65,15 +64,30 @@ var app = $.sammy(function() { $('#navPlayback').addClass('active'); }); - this.get(/\#\/queue\/(\d+)/, function() { - prepare(); + this.get(/\#\/queue\/(\d+)\/([^\/]+)\/(.*)/, function() { current_app = 'queue'; - $('#navQueue').addClass('active'); - $('#cardQueue').removeClass('hide'); - $('#panel-heading-queue').empty(); pagination = parseInt(this.params['splat'][0]); - $('#queueList').find("tr:gt(0)").remove(); - socket.send('MPD_API_GET_QUEUE,'+pagination); + var mpdtag = this.params['splat'][1]; + var searchstr = this.params['splat'][2]; + + if ($('#cardQueue').hasClass('hide')) { + prepare(); + if (searchstr == '') { + setPagination(pagination); + } + $('#searchtags2 > button').each(function() { + if ($(this).text == mpdtag) { $(this).removeClass('btn-secondary').addClass('btn-success'); } + }); + $('#cardQueue').removeClass('hide'); + $('#navQueue').addClass('active'); + } + $('#queueList > tbody').empty(); + if (searchstr.length >= 3) { + socket.send('MPD_API_SEARCH_QUEUE,' + mpdtag + ','+pagination+',' + searchstr); + } + else { + socket.send('MPD_API_GET_QUEUE,'+pagination); + } }); this.get(/\#\/browse\/playlists\/(\d+)/, function() { @@ -109,8 +123,8 @@ var app = $.sammy(function() { $('#cardBrowse').removeClass('hide'); $('#cardBrowseFilesystem').removeClass('hide'); $('#cardBrowseNavFilesystem').addClass('active'); - $('#browseFilesystemList').find("tr:gt(0)").remove(); - $('#browseBreadcrumb').empty().append("
  • root
  • "); + $('#browseFilesystemList > tbody').empty(); + $('#browseBreadcrumb').empty().append("
  • root
  • "); socket.send('MPD_API_GET_BROWSE,'+pagination+','+(browsepath ? browsepath : "/")); // Don't add all songs from root var add_all_songs = $('#add-all-songs'); @@ -123,7 +137,6 @@ var app = $.sammy(function() { } else { add_all_songs.addClass('hide'); } - //$('#panel-heading-browse').text("Browse database: /"+browsepath); var path_array = browsepath.split('/'); var full_path = ""; @@ -139,21 +152,35 @@ var app = $.sammy(function() { }); }); - this.get(/\#\/search\/(.*)/, function() { - - prepare(); + this.get(/\#\/search\/(\d+)\/([^\/]+)\/(.*)/, function() { current_app = 'search'; - $('#cardSearch').removeClass('hide'); - var searchstr = this.params['splat'][0]; - - $('#search > input').val(searchstr); - socket.send('MPD_API_SEARCH,' + searchstr); - $('#searchList').find("tr:gt(0)").remove(); - $('#searchList > tbody').append( - "search" + - "Searching" + - ""); - $('#panel-heading-search').text("Search: "+searchstr); + pagination = parseInt(this.params['splat'][0]); + var mpdtag = this.params['splat'][1]; + var searchstr = this.params['splat'][2]; + + if ($('#cardSearch').hasClass('hide')) { + prepare(); + if (searchstr != '') { + $('#searchList > tbody').append( + "search" + + "Searching" + + ""); + } + else { + setPagination(pagination); + } + $('#search > input').val(searchstr); + $('#searchstr2').val(searchstr); + $('#searchtags2 > button').each(function() { + if ($(this).text == mpdtag) { $(this).removeClass('btn-secondary').addClass('btn-success'); } + }); + $('#cardSearch').removeClass('hide'); + $('#navSearch').addClass('active'); + } + $('#searchList > tbody').empty(); + if (searchstr.length >= 3) { + socket.send('MPD_API_SEARCH,' + mpdtag + ','+pagination+',' + searchstr); + } }); this.get("/", function(context) { @@ -259,7 +286,7 @@ function webSocketConnect() { nrItems++; var minutes = Math.floor(obj.data[song].duration / 60); var seconds = obj.data[song].duration - minutes * 60; - + $('#queueList > tbody').append( "" + (obj.data[song].pos + 1) + "" + ""+ obj.data[song].title +"" + @@ -280,7 +307,7 @@ function webSocketConnect() { if ( isTouch ) { $('#queueList > tbody > tr > td:last-child').append( '' + + 'onclick="delQueueSong($(this).parents(\'tr\'));">' + 'delete'); } else { $('#queueList > tbody > tr').on({ @@ -294,7 +321,7 @@ function webSocketConnect() { if($(this).children().last().has("a").length == 0) $(this).children().last().append( '' + + 'onclick="delQueueSong($(this).parents(\'tr\'));">' + 'delete') .find('a').fadeTo('fast',1); }); @@ -335,17 +362,17 @@ function webSocketConnect() { setPagination(obj.totalEntities); if ( isTouch ) { $('#'+current_app+'List > tbody > tr > td:last-child').append( - "" + - "delete"); + '' + + 'delete'); } else { $('#'+current_app+'List > tbody > tr').on({ mouseover: function(){ - if($(this).children().last().has("a").length == 0) + if($(this).children().last().has('a').length == 0) $(this).children().last().append( - "" + - "delete"); + '' + + 'delete'); }, mouseleave: function(){ var doomed = $(this); @@ -368,7 +395,8 @@ function webSocketConnect() { } break; case 'search': - $('#searchList').find("tr:gt(0)").remove(); + $('#searchList > tbody').empty(); + $('#panel-heading-search').text(obj.totalEntities + ' Songs found'); case 'browse': if(current_app !== 'browseFilesystem' && current_app !== 'search') break; @@ -382,78 +410,32 @@ function webSocketConnect() { nrItems++; switch(obj.data[item].type) { case 'directory': - var clazz = 'dir'; - if (filter !== "") { - var first = obj.data[item].dir[0]; - if (filter === "num" && isNaN(first)) { - clazz += ' hide'; - } else if (filter >= "A" && filter <= "Z" && first.toUpperCase() !== filter) { - clazz += ' hide'; - } else if (filter === "plist") { - clazz += ' hide'; - } - } $('#'+current_app+'List > tbody').append( - "" + - "folder_open" + - "" + basename(obj.data[item].dir) + "" + - "" - ); - break; - case 'playlist': - var clazz = 'plist'; - if ( (filter !== "") && (filter !== "plist") ) { - clazz += ' hide'; - } - $('#'+current_app+'List > tbody').append( - "" + - "list" + - "" + basename(obj.data[item].plist) + "" + - "" + '' + + 'folder_open' + + '' + basename(obj.data[item].dir) + '' + + '' ); break; case 'song': var minutes = Math.floor(obj.data[item].duration / 60); var seconds = obj.data[item].duration - minutes * 60; - if (obj.data[item].artist == null) { - var artist = ""; - } else { - var artist = "" + obj.data[item].artist + - "" + obj.data[item].album + ""; - } - $('#'+current_app+'List > tbody').append( - "" + - "music_note" + - "" + obj.data[item].title + "" + - "" + obj.data[item].artist + "" + - "" + obj.data[item].album + "" + - "" + minutes + ":" + (seconds < 10 ? '0' : '') + seconds + - "" + '' + + 'music_note' + + '' + obj.data[item].title + '' + + '' + obj.data[item].artist + '' + + '' + obj.data[item].album + '' + + '' + minutes + ':' + (seconds < 10 ? '0' : '') + seconds + + '' ); break; - case 'wrap': - if(current_app == 'browseFilesystem') { - $('#browseNext').removeClass('disabled'); - $('#browsePagination').removeClass('hide'); - } else { - $('#'+current_app+'List > tbody').append( - "error_outline" + - "Too many results, please refine your search!" + - "" - ); - } - break; } - - if(pagination > 0) { - $('#browsePrev').removeClass('disabled'); - $('#browsePagination').removeClass('hide'); - } - } + setPagination(obj.totalEntities); + if (current_app == 'search') if (nrItems == 0) { $('#'+current_app+'List > tbody').append( @@ -497,18 +479,12 @@ function webSocketConnect() { pagination = 0; browsepath = $(this).attr("uri"); $("#browseFilesystemList > a").attr("href", '#/browse/filesystem/'+pagination+'/'+browsepath); - $('#filter > a').attr("href", '#/browse/filesystem/'+pagination+'/'+browsepath); app.setLocation('#/browse/filesystem/'+pagination+'/'+browsepath); - set_filter(''); break; case 'song': socket.send("MPD_API_ADD_TRACK," + decodeURI($(this).attr("uri"))); showNotification('"' + $('td:nth-last-child(3)', this).text() + '" added','','','success'); break; - case 'plist': - socket.send("MPD_API_ADD_PLAYLIST," + decodeURI($(this).attr("uri"))); - showNotification('"' + $('td:nth-last-child(3)', this).text() + '" added','','','success'); - break; } } }); @@ -518,9 +494,7 @@ function webSocketConnect() { pagination = 0; browsepath = $(this).attr("uri"); $("#browseFilesystemList > a").attr("href", '#/browse/filesystem/'+pagination+'/'+browsepath); - $('#filter > a').attr("href", '#/browse/filesystem/'+pagination+'/'+browsepath); app.setLocation('#/browse/filesystem/'+pagination+'/'+browsepath); - set_filter(''); } }); @@ -814,7 +788,7 @@ function setLocalStream(mpdhost,streamport) { Cookies.set('mpdstream', mpdstream, { expires: 424242 }); } -function trash(tr) { +function delQueueSong(tr) { if ( $('#btntrashmodeup').hasClass('btn-success') ) { socket.send('MPD_API_RM_RANGE,0,' + (tr.index() + 1)); tr.remove(); @@ -827,6 +801,11 @@ function trash(tr) { }; } +function delPlaylist(tr) { + socket.send("MPD_API_RM_PLAYLIST," + decodeURI(tr.attr("uri"))); + tr.remove(); +} + function basename(path) { return path.split('/').reverse()[0]; } @@ -925,10 +904,29 @@ $('#search > input').keypress(function (event) { }); $('#search').submit(function () { - app.setLocation("#/search/"+$('#search > input').val()); + doSearch($('#search > input').val()); return false; }); +function doSearch(searchstr) { + var mpdtag='Any Tag'; + $('#searchtags2 > button').each(function() { + if ($(this).hasClass('btn-success')) { mpdtag=$(this).text(); } + }); + app.setLocation('#/search/' + pagination + '/' + mpdtag + '/' + searchstr); +} + +$('#searchstr2').keyup(function (event) { + pagination=0; + doSearch($(this).val()); +}); + +$('#searchtags2 > button').on('click',function (e) { + $('#searchtags2 > button').removeClass('btn-success').addClass('btn-secondary'); + $(this).removeClass('btn-secondary').addClass('btn-success'); + doSearch($('#searchstr2').val()); +}); + $('#searchqueuestr').keyup(function (event) { pagination=0; doQueueSearch(); @@ -946,40 +944,13 @@ function doQueueSearch() { $('#searchqueuetag > button').each(function() { if ($(this).hasClass('btn-success')) { mpdtag=$(this).text(); } }); - - if (searchstr.length >= 3) { - socket.send('MPD_API_SEARCH_QUEUE,' + mpdtag + ','+pagination+',' + searchstr); - } - if (searchstr.length == 0) { - socket.send('MPD_API_GET_QUEUE,0'); - } + app.setLocation('#/queue/' + pagination + '/' + mpdtag + '/' + searchstr); } $('#searchqueue').submit(function () { return false; }); -$('.page-link').on('click', function (e) { - - switch ($(this).text()) { - case "Next": - pagination += MAX_ELEMENTS_PER_PAGE; - break; - case "Previous": - pagination -= MAX_ELEMENTS_PER_PAGE; - if(pagination <= 0) - pagination = 0; - break; - } - - switch(current_app) { - case "browseFilesystem": - app.setLocation('#/browse/filesystem/'+pagination+'/'+browsepath); - break; - } - e.preventDefault(); -}); - function scrollToTop() { document.body.scrollTop = 0; // For Safari document.documentElement.scrollTop = 0; // For Chrome, Firefox, IE and Opera @@ -1011,6 +982,9 @@ function gotoPage(x,element,event) { app.setLocation('#/queue/'+pagination); } break; + case "search": + doSearch($('#searchstr2').val()); + break; case "browseFilesystem": app.setLocation('#/browse/filesystem/'+pagination+'/'+browsepath); break; @@ -1127,51 +1101,11 @@ $(document).keydown(function(e){ e.preventDefault(); }); -function set_filter (c) { - filter = c; - $('#filter > a').removeClass('btn-success'); - $('#f' + c).addClass('btn-success'); - - if (filter === "") { - $('#'+current_app+'List > tbody > tr').removeClass('hide'); - } else if (filter === "plist") { - $('#'+current_app+'List > tbody > tr.dir').addClass('hide'); - $('#'+current_app+'List > tbody > tr.song').addClass('hide'); - $('#'+current_app+'List > tbody > tr.plist').removeClass('hide'); - } else { - $.each($('#'+current_app+'List > tbody > tr'), function(i, line) { - var first = basename($(line).attr('uri'))[0]; - if ( $(line).hasClass('song') ) { - first = $(line).children().eq(3).text()[0]; - } - - if (filter === "num") { - 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'); - } - } - }); - } -} - function add_filter () { - $('#filter').append('All'); - $('#filter').append('#'); - for (i = 65; i <= 90; i++) { var c = String.fromCharCode(i); - $('#filter').append('' + c + ''); + $('#browseFilesystemGotoLetters').append('' + c + ''); } - - $('#filter').append('list'); } function chVolume (increment) { diff --git a/src/mpd_client.c b/src/mpd_client.c index a7cab64..757da30 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -183,11 +183,11 @@ int callback_mpd(struct mg_connection *c) if((token = strtok(NULL, ",")) == NULL) goto out_browse; - free(p_charbuf); + free(p_charbuf); p_charbuf = strdup(c->content); n = mpd_put_browse(mpd.buf, get_arg2(p_charbuf), uint_buf); out_browse: - free(p_charbuf); + free(p_charbuf); break; case MPD_API_ADD_TRACK: p_charbuf = strdup(c->content); @@ -248,10 +248,32 @@ out_save_queue: free(p_charbuf); break; case MPD_API_SEARCH_QUEUE: - p_charbuf = strdup(c->content); if(strcmp(strtok(p_charbuf, ","), "MPD_API_SEARCH_QUEUE")) - goto out_search_queue; + goto out_search_queue; + if((token = strtok(NULL, ",")) == NULL) { + goto out_search_queue; + } else { + mpdtagtype = strdup(token); + } + + uint_buf = strtoul(strtok(NULL, ","), NULL, 10); + + if((token = strtok(NULL, ",")) == NULL) { + free(mpdtagtype); + goto out_search_queue; + } else { + searchstr = strdup(token); + } + n = mpd_search_queue(mpd.buf, mpdtagtype, uint_buf, searchstr); + free(searchstr); +out_search_queue: + free(p_charbuf); + break; + case MPD_API_SEARCH: + p_charbuf = strdup(c->content); + if(strcmp(strtok(p_charbuf, ","), "MPD_API_SEARCH")) + goto out_search; if((token = strtok(NULL, ",")) == NULL) { goto out_search_queue; } else { @@ -266,25 +288,8 @@ out_save_queue: } else { searchstr = strdup(token); } - - //free(p_charbuf); - //p_charbuf = strdup(c->content); - n = mpd_search_queue(mpd.buf, mpdtagtype, uint_buf, searchstr); + n = mpd_search(mpd.buf, mpdtagtype, uint_buf, searchstr); free(searchstr); -out_search_queue: - free(p_charbuf); - break; - case MPD_API_SEARCH: - p_charbuf = strdup(c->content); - if(strcmp(strtok(p_charbuf, ","), "MPD_API_SEARCH")) - goto out_search; - - if((token = strtok(NULL, ",")) == NULL) - goto out_search; - - free(p_charbuf); - p_charbuf = strdup(c->content); - n = mpd_search(mpd.buf, get_arg1(p_charbuf)); out_search: free(p_charbuf); break; @@ -312,6 +317,20 @@ out_search: out_send_message: free(p_charbuf); break; + case MPD_API_RM_PLAYLIST: + p_charbuf = strdup(c->content); + if(strcmp(strtok(p_charbuf, ","), "MPD_API_RM_PLAYLIST")) + goto out_rm_playlist; + + if((token = strtok(NULL, ",")) == NULL) + goto out_rm_playlist; + + free(p_charbuf); + p_charbuf = strdup(c->content); + mpd_run_rm(mpd.conn, get_arg1(p_charbuf)); +out_rm_playlist: + free(p_charbuf); + break; case MPD_API_GET_OPTIONS: n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"mpdoptions\", \"data\": " "{\"mpdhost\" : \"%s\", \"mpdport\": \"%d\", \"passwort_set\": %s, " @@ -713,58 +732,38 @@ int mpd_put_browse(char *buffer, char *path, unsigned int offset) while((entity = mpd_recv_entity(mpd.conn)) != NULL) { const struct mpd_song *song; const struct mpd_directory *dir; - const struct mpd_playlist *pl; + entity_count++; - if(offset > entity_count) - { - mpd_entity_free(entity); - entity_count++; - continue; - } - else if(offset + MAX_ELEMENTS_PER_PAGE - 1 < entity_count) - { - mpd_entity_free(entity); - cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"wrap\",\"count\":"); - cur += json_emit_int(cur, end - cur, entity_count); - cur += json_emit_raw_str(cur, end - cur, "} "); - break; - } + if(entity_count > offset && entity_count <= offset+MAX_ELEMENTS_PER_PAGE) { + switch (mpd_entity_get_type(entity)) { + case MPD_ENTITY_TYPE_UNKNOWN: + break; - switch (mpd_entity_get_type(entity)) { - case MPD_ENTITY_TYPE_UNKNOWN: - break; + case MPD_ENTITY_TYPE_SONG: + song = mpd_entity_get_song(entity); + cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"song\",\"uri\":"); + cur += json_emit_quoted_str(cur, end - cur, mpd_song_get_uri(song)); + cur += json_emit_raw_str(cur, end - cur, ",\"album\":"); + cur += json_emit_quoted_str(cur, end - cur, mpd_get_album(song)); + cur += json_emit_raw_str(cur, end - cur, ",\"artist\":"); + cur += json_emit_quoted_str(cur, end - cur, mpd_get_artist(song)); + cur += json_emit_raw_str(cur, end - cur, ",\"duration\":"); + cur += json_emit_int(cur, end - cur, mpd_song_get_duration(song)); + cur += json_emit_raw_str(cur, end - cur, ",\"title\":"); + cur += json_emit_quoted_str(cur, end - cur, mpd_get_title(song)); + cur += json_emit_raw_str(cur, end - cur, "},"); + break; - case MPD_ENTITY_TYPE_SONG: - song = mpd_entity_get_song(entity); - cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"song\",\"uri\":"); - cur += json_emit_quoted_str(cur, end - cur, mpd_song_get_uri(song)); - cur += json_emit_raw_str(cur, end - cur, ",\"album\":"); - cur += json_emit_quoted_str(cur, end - cur, mpd_get_album(song)); - cur += json_emit_raw_str(cur, end - cur, ",\"artist\":"); - cur += json_emit_quoted_str(cur, end - cur, mpd_get_artist(song)); -// cur += json_emit_raw_str(cur, end - cur, ",\"album_artist\":"); -// cur += json_emit_quoted_str(cur, end - cur, mpd_get_album_artist(song)); - cur += json_emit_raw_str(cur, end - cur, ",\"duration\":"); - cur += json_emit_int(cur, end - cur, mpd_song_get_duration(song)); - cur += json_emit_raw_str(cur, end - cur, ",\"title\":"); - cur += json_emit_quoted_str(cur, end - cur, mpd_get_title(song)); - cur += json_emit_raw_str(cur, end - cur, "},"); - break; + case MPD_ENTITY_TYPE_DIRECTORY: + dir = mpd_entity_get_directory(entity); + cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"directory\",\"dir\":"); + cur += json_emit_quoted_str(cur, end - cur, mpd_directory_get_path(dir)); + cur += json_emit_raw_str(cur, end - cur, "},"); + break; - case MPD_ENTITY_TYPE_DIRECTORY: - dir = mpd_entity_get_directory(entity); - - cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"directory\",\"dir\":"); - cur += json_emit_quoted_str(cur, end - cur, mpd_directory_get_path(dir)); - cur += json_emit_raw_str(cur, end - cur, "},"); - break; - - case MPD_ENTITY_TYPE_PLAYLIST: - pl = mpd_entity_get_playlist(entity); - cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"playlist\",\"plist\":"); - cur += json_emit_quoted_str(cur, end - cur, mpd_playlist_get_path(pl)); - cur += json_emit_raw_str(cur, end - cur, "},"); - break; + case MPD_ENTITY_TYPE_PLAYLIST: + break; + } } mpd_entity_free(entity); entity_count++; @@ -778,8 +777,9 @@ int mpd_put_browse(char *buffer, char *path, unsigned int offset) /* remove last ',' */ cur--; - - cur += json_emit_raw_str(cur, end - cur, "]}"); + cur += json_emit_raw_str(cur, end - cur, "],\"totalEntities\":"); + cur += json_emit_int(cur, end - cur, entity_count); + cur += json_emit_raw_str(cur, end - cur, "}"); return cur - buffer; } @@ -822,25 +822,36 @@ int mpd_put_playlists(char *buffer, unsigned int offset) return cur - buffer; } -int mpd_search(char *buffer, char *searchstr) +int mpd_search(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr) { char *cur = buffer; const char *end = buffer + MAX_SIZE; struct mpd_song *song; - unsigned long totalSongs = 0; + unsigned long entity_count = 0; + unsigned long entities_returned = 0; - if(mpd_search_db_songs(mpd.conn, false) == false) - RETURN_ERROR_AND_RECOVER("mpd_search_db_songs"); - else if(mpd_search_add_any_tag_constraint(mpd.conn, MPD_OPERATOR_DEFAULT, searchstr) == false) - RETURN_ERROR_AND_RECOVER("mpd_search_add_any_tag_constraint"); - else if(mpd_search_commit(mpd.conn) == false) + if(mpd_search_db_songs(mpd.conn, false) == false) { + RETURN_ERROR_AND_RECOVER("mpd_search_queue_songs"); + } + + if (mpd_tag_name_parse(mpdtagtype) != MPD_TAG_UNKNOWN) { + if (mpd_search_add_tag_constraint(mpd.conn, MPD_OPERATOR_DEFAULT, mpd_tag_name_parse(mpdtagtype), searchstr) == false) + RETURN_ERROR_AND_RECOVER("mpd_search_add_tag_constraint"); + } + else { + if (mpd_search_add_any_tag_constraint(mpd.conn, MPD_OPERATOR_DEFAULT, searchstr) == false) + RETURN_ERROR_AND_RECOVER("mpd_search_add_any_tag_constraint"); + } + + if(mpd_search_commit(mpd.conn) == false) RETURN_ERROR_AND_RECOVER("mpd_search_commit"); else { cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"search\",\"data\":[ "); while((song = mpd_recv_song(mpd.conn)) != NULL) { - totalSongs ++; - if (totalSongs <=300) { + entity_count ++; + if(entity_count > offset && entity_count <= offset+MAX_ELEMENTS_PER_PAGE) { + entities_returned ++; cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"song\""); cur += json_emit_raw_str(cur, end - cur, ",\"uri\":"); cur += json_emit_quoted_str(cur, end - cur, mpd_song_get_uri(song)); @@ -848,8 +859,6 @@ int mpd_search(char *buffer, char *searchstr) cur += json_emit_quoted_str(cur, end - cur, mpd_get_album(song)); cur += json_emit_raw_str(cur, end - cur, ",\"artist\":"); cur += json_emit_quoted_str(cur, end - cur, mpd_get_artist(song)); -// cur += json_emit_raw_str(cur, end - cur, ",\"album_artist\":"); -// cur += json_emit_quoted_str(cur, end - cur, mpd_get_album_artist(song)); cur += json_emit_raw_str(cur, end - cur, ",\"duration\":"); cur += json_emit_int(cur, end - cur, mpd_song_get_duration(song)); cur += json_emit_raw_str(cur, end - cur, ",\"title\":"); @@ -859,18 +868,18 @@ int mpd_search(char *buffer, char *searchstr) mpd_song_free(song); } - /* Maximum results */ - if(totalSongs++ > 300) - { - cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"wrap\"},"); - } - /* remove last ',' */ cur--; cur += json_emit_raw_str(cur, end - cur, "]"); - cur += json_emit_raw_str(cur, end - cur, ",\"totalSongs\":"); - cur += json_emit_int(cur, end - cur, totalSongs); + cur += json_emit_raw_str(cur, end - cur, ",\"totalEntities\":"); + cur += json_emit_int(cur, end - cur, entity_count); + cur += json_emit_raw_str(cur, end - cur, ",\"offset\":"); + cur += json_emit_int(cur, end - cur, offset); + cur += json_emit_raw_str(cur, end - cur, ",\"returnedEntities\":"); + cur += json_emit_int(cur, end - cur, entities_returned); + cur += json_emit_raw_str(cur, end - cur, ",\"mpdtagtype\":"); + cur += json_emit_quoted_str(cur, end - cur, mpdtagtype); cur += json_emit_raw_str(cur, end - cur, "}"); } return cur - buffer; @@ -905,8 +914,6 @@ int mpd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char * totalSongs ++; if(totalSongs > offset && totalSongs <= offset+MAX_ELEMENTS_PER_PAGE) { cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"song\""); -// cur += json_emit_raw_str(cur, end - cur, ",\"uri\":"); -// cur += json_emit_quoted_str(cur, end - cur, mpd_song_get_uri(song)); cur += json_emit_raw_str(cur, end - cur, ",\"id\":"); cur += json_emit_int(cur, end - cur, mpd_song_get_id(song)); cur += json_emit_raw_str(cur, end - cur, ",\"pos\":"); @@ -915,8 +922,6 @@ int mpd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char * cur += json_emit_quoted_str(cur, end - cur, mpd_get_album(song)); cur += json_emit_raw_str(cur, end - cur, ",\"artist\":"); cur += json_emit_quoted_str(cur, end - cur, mpd_get_artist(song)); -// cur += json_emit_raw_str(cur, end - cur, ",\"album_artist\":"); -// cur += json_emit_quoted_str(cur, end - cur, mpd_get_album_artist(song)); cur += json_emit_raw_str(cur, end - cur, ",\"duration\":"); cur += json_emit_int(cur, end - cur, mpd_song_get_duration(song)); cur += json_emit_raw_str(cur, end - cur, ",\"title\":"); @@ -926,15 +931,13 @@ int mpd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char * } } -/* if (totalSongs > 100) { - cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"wrap\"},"); - } -*/ cur--; cur += json_emit_raw_str(cur, end - cur, "]"); cur += json_emit_raw_str(cur, end - cur, ",\"totalSongs\":"); cur += json_emit_int(cur, end - cur, totalSongs); + cur += json_emit_raw_str(cur, end - cur, ",\"mpdtagtype\":"); + cur += json_emit_quoted_str(cur, end - cur, mpdtagtype); cur += json_emit_raw_str(cur, end - cur, "}"); } return cur - buffer; diff --git a/src/mpd_client.h b/src/mpd_client.h index 07eea06..7a0eac0 100644 --- a/src/mpd_client.h +++ b/src/mpd_client.h @@ -77,7 +77,8 @@ X(MPD_API_GET_STATS) \ X(MPD_API_SET_MIXRAMPDB) \ X(MPD_API_SET_MIXRAMPDELAY) \ - X(MPD_API_GET_PLAYLISTS) + X(MPD_API_GET_PLAYLISTS) \ + X(MPD_API_RM_PLAYLIST) enum mpd_cmd_ids { MPD_CMDS(GEN_ENUM) @@ -128,7 +129,7 @@ int mpd_put_current_song(char *buffer); int mpd_put_queue(char *buffer, unsigned int offset); int mpd_put_playlists(char *buffer, unsigned int offset); int mpd_put_browse(char *buffer, char *path, unsigned int offset); -int mpd_search(char *buffer, char *searchstr); +int mpd_search(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr); int mpd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr); int mympd_get_stats(); void mpd_disconnect(); From 8c423f4c770fb44f78560f8e7354b290c1b0cf9a Mon Sep 17 00:00:00 2001 From: jcorporation Date: Tue, 29 May 2018 23:05:34 +0200 Subject: [PATCH 05/23] New Settings API replaces Options API, Smaller Stats API --- htdocs/index.html | 2 +- htdocs/js/mpd.js | 129 ++++++++++++++++++++++++---------------------- src/config.h.in | 7 +-- src/mpd_client.c | 58 ++++++++++++++------- src/mpd_client.h | 5 +- src/mympd.c | 4 +- 6 files changed, 116 insertions(+), 89 deletions(-) diff --git a/htdocs/index.html b/htdocs/index.html index 807b570..9dd545f 100644 --- a/htdocs/index.html +++ b/htdocs/index.html @@ -515,7 +515,7 @@

    play_circle_outline myMPD – MPD Web GUI - written in C, utilizing Websockets and Bootstrap/JS

    myMPD is a lightweight MPD web client that runs without a dedicated webserver or interpreter. It's tuned for minimal resource usage and requires only very litte dependencies. myMPD is a fork of ympd.

    diff --git a/htdocs/js/mpd.js b/htdocs/js/mpd.js index 4cac5ef..a55f6ed 100644 --- a/htdocs/js/mpd.js +++ b/htdocs/js/mpd.js @@ -210,11 +210,16 @@ $(document).ready(function(){ $('#about').on('shown.bs.modal', function () { socket.send("MPD_API_GET_STATS"); - }) + }) + + $('#settings').on('shown.bs.modal', function () { + socket.send("MPD_API_GET_SETTINGS"); + }) $('#addstream').on('shown.bs.modal', function () { $('#streamurl').focus(); - }) + }) + $('#addstream form').on('submit', function (e) { addStream(); }); @@ -249,8 +254,8 @@ function webSocketConnect() { showNotification('Connected to myMPD','','','success'); $('#modalConnectionError').modal('hide'); app.run(); - /* emit request for mympd options */ - socket.send('MPD_API_GET_OPTIONS'); + /* emit request for mympd settings */ + socket.send('MPD_API_GET_SETTINGS'); /* emit initial request for output names */ socket.send('MPD_API_GET_OUTPUTS'); @@ -534,62 +539,7 @@ function webSocketConnect() { $('#queueList > tbody > tr[trackid='+obj.data.currentsongid+'] > td').eq(0).addClass('material-icons').text('play_arrow'); $('#queueList > tbody > tr[trackid='+obj.data.currentsongid+']').addClass('active').css("font-weight", "bold"); - if(obj.data.random) - $('#btnrandom').removeClass('btn-secondary').addClass("btn-success") - else - $('#btnrandom').removeClass("btn-success").addClass("btn-secondary"); - - if(obj.data.consume) - $('#btnconsume').removeClass('btn-secondary').addClass("btn-success") - else - $('#btnconsume').removeClass("btn-success").addClass("btn-secondary"); - - if(obj.data.single) - $('#btnsingle').removeClass('btn-secondary').addClass("btn-success") - else - $('#btnsingle').removeClass("btn-success").addClass("btn-secondary"); - - if(obj.data.crossfade != undefined) { - if (!last_state) { - $('#inputCrossfade').removeAttr('disabled'); - $('#inputCrossfade').val(obj.data.crossfade); - } else if(obj.data.crossfade != last_state.data.crossfade) { - $('#inputCrossfade').removeAttr('disabled'); - $('#inputCrossfade').val(obj.data.crossfade); - } - } else { - $('#inputCrossfade').attr('disabled', 'disabled'); - } - if(obj.data.mixrampdb != undefined) { - if (!last_state) { - $('#inputMixrampdb').removeAttr('disabled'); - $('#inputMixrampdb').val(obj.data.mixrampdb); - } else if(obj.data.mixrampdb != last_state.data.mixrampdb) { - $('#inputMixrampdb').removeAttr('disabled'); - $('#inputMixrampdb').val(obj.data.mixrampdb); - } - } else { - $('#inputMixrampdb').attr('disabled', 'disabled'); - } - - if(obj.data.mixrampdelay != undefined) { - if (!last_state) { - $('#inputMixrampdelay').removeAttr('disabled'); - $('#inputMixrampdelay').val(obj.data.mixrampdelay); - } else if(obj.data.mixrampdelay != last_state.data.mixrampdelay) { - $('#inputMixrampdelay').removeAttr('disabled'); - $('#inputMixrampdelay').val(obj.data.mixrampdelay); - } - } else { - $('#inputMixrampdb').attr('disabled', 'disabled'); - } - - if(obj.data.repeat) - $('#btnrepeat').removeClass('btn-secondary').addClass("btn-success") - else - $('#btnrepeat').removeClass("btn-success").addClass("btn-secondary"); - last_state = obj; break; case 'outputnames': @@ -629,7 +579,48 @@ function webSocketConnect() { case "song_change": songChange(obj.data.title, obj.data.artist, obj.data.album, obj.data.uri); break; - case 'mpdoptions': + case 'settings': + if(obj.data.random) + $('#btnrandom').removeClass('btn-secondary').addClass("btn-success") + else + $('#btnrandom').removeClass("btn-success").addClass("btn-secondary"); + + if(obj.data.consume) + $('#btnconsume').removeClass('btn-secondary').addClass("btn-success") + else + $('#btnconsume').removeClass("btn-success").addClass("btn-secondary"); + + if(obj.data.single) + $('#btnsingle').removeClass('btn-secondary').addClass("btn-success") + else + $('#btnsingle').removeClass("btn-success").addClass("btn-secondary"); + + if(obj.data.crossfade != undefined) { + $('#inputCrossfade').removeAttr('disabled'); + $('#inputCrossfade').val(obj.data.crossfade); + } else { + $('#inputCrossfade').attr('disabled', 'disabled'); + } + + if(obj.data.mixrampdb != undefined) { + $('#inputMixrampdb').removeAttr('disabled'); + $('#inputMixrampdb').val(obj.data.mixrampdb); + } else { + $('#inputMixrampdb').attr('disabled', 'disabled'); + } + + if(obj.data.mixrampdelay != undefined) { + $('#inputMixrampdelay').removeAttr('disabled'); + $('#inputMixrampdelay').val(obj.data.mixrampdelay); + } else { + $('#inputMixrampdb').attr('disabled', 'disabled'); + } + + if(obj.data.repeat) + $('#btnrepeat').removeClass('btn-secondary').addClass("btn-success") + else + $('#btnrepeat').removeClass("btn-success").addClass("btn-secondary"); + setLocalStream(obj.data.mpdhost,obj.data.streamport); coverImageFile=obj.data.coverimage; break; @@ -642,6 +633,7 @@ function webSocketConnect() { $('#mpdstats_uptime').text(beautifyDuration(obj.data.uptime)); var d = new Date(obj.data.dbupdated * 1000); $('#mpdstats_dbupdated').text(d.toUTCString()); + $('#mympdVersion').text(obj.data.mympd_version); break; case 'error': showNotification(obj.data,'','','danger'); @@ -810,20 +802,31 @@ function basename(path) { return path.split('/').reverse()[0]; } -$('#btnrandom').on('click', function (e) { - socket.send("MPD_API_TOGGLE_RANDOM," + ($(this).hasClass('btn-success') ? 0 : 1)); +function toggleBtn(btn) { + if ($(btn).hasClass('btn-success')) { + $(btn).removeClass('btn-success').addClass('btn-secondary'); + } + else { + $(btn).removeClass('btn-secondary').addClass('btn-success'); + } +} +$('#btnrandom').on('click', function (e) { + socket.send("MPD_API_TOGGLE_RANDOM," + ($(this).hasClass('btn-success') ? 0 : 1)); + toggleBtn(this); }); $('#btnconsume').on('click', function (e) { socket.send("MPD_API_TOGGLE_CONSUME," + ($(this).hasClass('btn-success') ? 0 : 1)); - + toggleBtn(this); }); $('#btnsingle').on('click', function (e) { socket.send("MPD_API_TOGGLE_SINGLE," + ($(this).hasClass('btn-success') ? 0 : 1)); + toggleBtn(this); }); $('#btnrepeat').on('click', function (e) { socket.send("MPD_API_TOGGLE_REPEAT," + ($(this).hasClass('btn-success') ? 0 : 1)); + toggleBtn(this); }); function confirmSettings() { diff --git a/src/config.h.in b/src/config.h.in index ac957bb..5cb0adc 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -25,8 +25,9 @@ #ifndef __CONFIG_H__ #define __CONFIG_H__ -#define YMPD_VERSION_MAJOR ${CPACK_PACKAGE_VERSION_MAJOR} -#define YMPD_VERSION_MINOR ${CPACK_PACKAGE_VERSION_MINOR} -#define YMPD_VERSION_PATCH ${CPACK_PACKAGE_VERSION_PATCH} +#define MYMPD_VERSION_MAJOR ${CPACK_PACKAGE_VERSION_MAJOR} +#define MYMPD_VERSION_MINOR ${CPACK_PACKAGE_VERSION_MINOR} +#define MYMPD_VERSION_PATCH ${CPACK_PACKAGE_VERSION_PATCH} +#define MYMPD_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}" #define SRC_PATH "${ASSETS_PATH}" #endif diff --git a/src/mpd_client.c b/src/mpd_client.c index 757da30..b89bada 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -331,11 +331,8 @@ out_send_message: out_rm_playlist: free(p_charbuf); break; - case MPD_API_GET_OPTIONS: - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"mpdoptions\", \"data\": " - "{\"mpdhost\" : \"%s\", \"mpdport\": \"%d\", \"passwort_set\": %s, " - "\"streamport\": \"%d\",\"coverimage\": \"%s\"}" - "}", mpd.host, mpd.port, mpd.password ? "true" : "false", streamport, coverimage); + case MPD_API_GET_SETTINGS: + n = mympd_put_settings(mpd.buf); break; case MPD_API_GET_STATS: n = mympd_get_stats(mpd.buf); @@ -563,26 +560,17 @@ int mpd_put_state(char *buffer, int *current_song_id, int *next_song_id, unsign len = snprintf(buffer, MAX_SIZE, "{\"type\":\"state\", \"data\":{" - " \"state\":%d, \"volume\":%d, \"repeat\":%d," - " \"single\":%d, \"crossfade\":%d, \"consume\":%d, \"random\":%d, " - " \"songpos\": %d, \"elapsedTime\": %d, \"totalTime\":%d, " - " \"currentsongid\": %d, \"kbitrate\": %d, \"mixrampdb\": %lf, " - " \"mixrampdelay\": %lf, \"queue_length\": %d, \"nextsongpos\": %d" + "\"state\":%d, \"volume\":%d, \"songpos\": %d, \"elapsedTime\": %d, " + "\"totalTime\":%d, \"currentsongid\": %d, \"kbitrate\": %d, " + "\"queue_length\": %d, \"nextsongpos\": %d" "}}", mpd_status_get_state(status), mpd_status_get_volume(status), - mpd_status_get_repeat(status), - mpd_status_get_single(status), - mpd_status_get_crossfade(status), - mpd_status_get_consume(status), - mpd_status_get_random(status), mpd_status_get_song_pos(status), mpd_status_get_elapsed_time(status), mpd_status_get_total_time(status), mpd_status_get_song_id(status), mpd_status_get_kbit_rate(status), - mpd_status_get_mixrampdb(status), - mpd_status_get_mixrampdelay(status), mpd_status_get_queue_length(status), mpd_status_get_next_song_pos(status) ); @@ -594,6 +582,38 @@ int mpd_put_state(char *buffer, int *current_song_id, int *next_song_id, unsign return len; } +int mympd_put_settings(char *buffer) +{ + struct mpd_status *status; + int len; + + status = mpd_run_status(mpd.conn); + if (!status) { + fprintf(stderr, "MPD mpd_run_status: %s\n", mpd_connection_get_error_message(mpd.conn)); + mpd.conn_state = MPD_FAILURE; + return 0; + } + + len = snprintf(buffer, MAX_SIZE, + "{\"type\":\"settings\", \"data\":{" + "\"repeat\":%d, \"single\":%d, \"crossfade\":%d, \"consume\":%d, \"random\":%d, " + "\"mixrampdb\": %lf, \"mixrampdelay\": %lf, \"mpdhost\" : \"%s\", \"mpdport\": \"%d\", \"passwort_set\": %s, " + "\"streamport\": \"%d\",\"coverimage\": \"%s\"" + "}}", + mpd_status_get_repeat(status), + mpd_status_get_single(status), + mpd_status_get_crossfade(status), + mpd_status_get_consume(status), + mpd_status_get_random(status), + mpd_status_get_mixrampdb(status), + mpd_status_get_mixrampdelay(status), + mpd.host, mpd.port, mpd.password ? "true" : "false", streamport, coverimage + ); + mpd_status_free(status); + return len; +} + + int mpd_put_outputs(char *buffer, int names) { struct mpd_output *out; @@ -964,7 +984,9 @@ int mympd_get_stats(char *buffer) cur += json_emit_raw_str(cur, end - cur, ",\"dbupdated\":"); cur += json_emit_int(cur, end - cur, mpd_stats_get_db_update_time(stats)); cur += json_emit_raw_str(cur, end - cur, ",\"dbplaytime\":"); - cur += json_emit_int(cur, end - cur, mpd_stats_get_db_play_time(stats)); + cur += json_emit_int(cur, end - cur, mpd_stats_get_db_play_time(stats)); + cur += json_emit_raw_str(cur, end - cur, ",\"mympd_version\":"); + cur += json_emit_quoted_str(cur, end - cur, MYMPD_VERSION); cur += json_emit_raw_str(cur, end - cur, "}}"); mpd_stats_free(stats); diff --git a/src/mpd_client.h b/src/mpd_client.h index 7a0eac0..78a83f6 100644 --- a/src/mpd_client.h +++ b/src/mpd_client.h @@ -72,7 +72,7 @@ X(MPD_API_TOGGLE_SINGLE) \ X(MPD_API_SET_CROSSFADE) \ X(MPD_API_TOGGLE_REPEAT) \ - X(MPD_API_GET_OPTIONS) \ + X(MPD_API_GET_SETTINGS) \ X(MPD_API_SEND_SHUFFLE) \ X(MPD_API_GET_STATS) \ X(MPD_API_SET_MIXRAMPDB) \ @@ -131,7 +131,8 @@ int mpd_put_playlists(char *buffer, unsigned int offset); int mpd_put_browse(char *buffer, char *path, unsigned int offset); int mpd_search(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr); int mpd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr); -int mympd_get_stats(); +int mympd_get_stats(char *buffer); +int mympd_put_settings(char *buffer); void mpd_disconnect(); #endif diff --git a/src/mympd.c b/src/mympd.c index 4364387..65a25af 100644 --- a/src/mympd.c +++ b/src/mympd.c @@ -146,8 +146,8 @@ int main(int argc, char **argv) case 'v': fprintf(stdout, "myMPD %d.%d.%d\n" "Copyright (C) 2018 Juergen Mang \n" - "built " __DATE__ " "__TIME__ " ("__VERSION__")\n", - YMPD_VERSION_MAJOR, YMPD_VERSION_MINOR, YMPD_VERSION_PATCH); + "built " __DATE__ " "__TIME__ "\n", + MYMPD_VERSION_MAJOR, MYMPD_VERSION_MINOR, MYMPD_VERSION_PATCH); return EXIT_SUCCESS; break; default: From 707b8b1068ccc0ff59954da8f562cbd4c3f621fb Mon Sep 17 00:00:00 2001 From: jcorporation Date: Tue, 29 May 2018 23:20:14 +0200 Subject: [PATCH 06/23] Bump version to 2.2.0 --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 562f9d3..1d918bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,8 +3,8 @@ cmake_minimum_required(VERSION 2.6) project (mympd C) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/") set(CPACK_PACKAGE_VERSION_MAJOR "2") -set(CPACK_PACKAGE_VERSION_MINOR "1") -set(CPACK_PACKAGE_VERSION_PATCH "1") +set(CPACK_PACKAGE_VERSION_MINOR "2") +set(CPACK_PACKAGE_VERSION_PATCH "0") if(CMAKE_BUILD_TYPE MATCHES RELEASE) set(ASSETS_PATH "${CMAKE_INSTALL_PREFIX}/share/${PROJECT_NAME}/htdocs") else() From 4802b417aafd16a6a75e079f0f08f03730288504 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Tue, 29 May 2018 23:28:11 +0200 Subject: [PATCH 07/23] Fixed version option --- README.md | 2 +- mympd.1 | 2 +- src/mympd.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e4afbe2..2ef3201 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ Usage: ./mympd [OPTION]... -u, --user drop priviliges to user after socket bind -m, --mpdpass specifies the password to use when connecting to mpd -i, --coverimage filename for coverimage [folder.jpg] - -V, --version get version + -v, --version get version --help this help ``` diff --git a/mympd.1 b/mympd.1 index 80d2911..8851257 100644 --- a/mympd.1 +++ b/mympd.1 @@ -39,7 +39,7 @@ specifies the password to use when connecting to mpd \fB-i\fR, \fB\-\-coverimage FILENAME\fR filename for coverimage [folder.jpg] .TP -\fB\-V\fR, \fB\-\-version\fR +\fB\-v\fR, \fB\-\-version\fR print version and exit .TP \fB\-\-help\fR diff --git a/src/mympd.c b/src/mympd.c index 65a25af..f6a244e 100644 --- a/src/mympd.c +++ b/src/mympd.c @@ -112,7 +112,7 @@ int main(int argc, char **argv) {0, 0, 0, 0 } }; - while((n = getopt_long(argc, argv, "D:h:p:l:w:u:d:v:m:s:i:", + while((n = getopt_long(argc, argv, "D:h:p:l:w:u:d:vm:s:i:", long_options, &option_index)) != -1) { switch (n) { case 'D': @@ -146,7 +146,7 @@ int main(int argc, char **argv) case 'v': fprintf(stdout, "myMPD %d.%d.%d\n" "Copyright (C) 2018 Juergen Mang \n" - "built " __DATE__ " "__TIME__ "\n", + "Built " __DATE__ " "__TIME__ "\n", MYMPD_VERSION_MAJOR, MYMPD_VERSION_MINOR, MYMPD_VERSION_PATCH); return EXIT_SUCCESS; break; From 994f6d2daa14875d8fb74742287d26cd7c40bb8b Mon Sep 17 00:00:00 2001 From: jcorporation Date: Tue, 29 May 2018 23:55:24 +0200 Subject: [PATCH 08/23] Get MAX_ELEMENTS_PER_PAGE from central config (Settings API) --- htdocs/js/mpd.js | 9 ++++--- src/mpd_client.c | 70 ++++++++++++++++++++++++++++++------------------ 2 files changed, 50 insertions(+), 29 deletions(-) diff --git a/htdocs/js/mpd.js b/htdocs/js/mpd.js index a55f6ed..95d8dba 100644 --- a/htdocs/js/mpd.js +++ b/htdocs/js/mpd.js @@ -278,8 +278,8 @@ function webSocketConnect() { break; $('#panel-heading-queue').empty(); - if (obj.totalSongs > 0) { - $('#panel-heading-queue').text(obj.totalSongs+' Songs'); + if (obj.totalEntities > 0) { + $('#panel-heading-queue').text(obj.totalEntities+' Songs'); } if (typeof(obj.totalTime) != undefined && obj.totalTime > 0 ) { $('#panel-heading-queue').append(' – ' + beautifyDuration(obj.totalTime)); @@ -307,7 +307,7 @@ function webSocketConnect() { "" ); } - setPagination(obj.totalSongs); + setPagination(obj.totalEntities); if ( isTouch ) { $('#queueList > tbody > tr > td:last-child').append( @@ -580,6 +580,9 @@ function webSocketConnect() { songChange(obj.data.title, obj.data.artist, obj.data.album, obj.data.uri); break; case 'settings': + if (!isNaN(obj.data.max_elements_per_page)) { + MAX_ELEMENTS_PER_PAGE=obj.data.max_elements_per_page; + } if(obj.data.random) $('#btnrandom').removeClass('btn-secondary').addClass("btn-success") else diff --git a/src/mpd_client.c b/src/mpd_client.c index b89bada..ed2c085 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -598,7 +598,7 @@ int mympd_put_settings(char *buffer) "{\"type\":\"settings\", \"data\":{" "\"repeat\":%d, \"single\":%d, \"crossfade\":%d, \"consume\":%d, \"random\":%d, " "\"mixrampdb\": %lf, \"mixrampdelay\": %lf, \"mpdhost\" : \"%s\", \"mpdport\": \"%d\", \"passwort_set\": %s, " - "\"streamport\": \"%d\",\"coverimage\": \"%s\"" + "\"streamport\": \"%d\",\"coverimage\": \"%s\", \"max_elements_per_page\": %d" "}}", mpd_status_get_repeat(status), mpd_status_get_single(status), @@ -607,7 +607,10 @@ int mympd_put_settings(char *buffer) mpd_status_get_random(status), mpd_status_get_mixrampdb(status), mpd_status_get_mixrampdelay(status), - mpd.host, mpd.port, mpd.password ? "true" : "false", streamport, coverimage + mpd.host, mpd.port, + mpd.password ? "true" : "false", + streamport, coverimage, + MAX_ELEMENTS_PER_PAGE ); mpd_status_free(status); return len; @@ -661,17 +664,14 @@ int mpd_put_current_song(char *buffer) cur += json_emit_int(cur, end - cur, mpd_song_get_pos(song)); cur += json_emit_raw_str(cur, end - cur, ",\"title\":"); cur += json_emit_quoted_str(cur, end - cur, mpd_get_title(song)); -// cur += json_emit_raw_str(cur, end - cur, ",\"album_artist\":"); -// cur += json_emit_quoted_str(cur, end - cur, mpd_get_album_artist(song)); cur += json_emit_raw_str(cur, end - cur, ",\"artist\":"); cur += json_emit_quoted_str(cur, end - cur, mpd_get_artist(song)); cur += json_emit_raw_str(cur, end - cur, ",\"album\":"); cur += json_emit_quoted_str(cur, end - cur, mpd_get_album(song)); - cur += json_emit_raw_str(cur, end - cur, ",\"uri\":"); cur += json_emit_quoted_str(cur, end - cur, mpd_song_get_uri(song)); - cur += json_emit_raw_str(cur, end - cur, "}}"); + mpd_song_free(song); mpd_response_finish(mpd.conn); @@ -684,9 +684,9 @@ int mpd_put_queue(char *buffer, unsigned int offset) const char *end = buffer + MAX_SIZE; struct mpd_entity *entity; unsigned long totalTime = 0; - unsigned long totalSongs = 0; + unsigned long entity_count = 0; + unsigned long entities_returned = 0; - /*get complete queue for total songs*/ if (!mpd_send_list_queue_range_meta(mpd.conn, 0, -1)) RETURN_ERROR_AND_RECOVER("mpd_send_list_queue_meta"); @@ -700,9 +700,9 @@ int mpd_put_queue(char *buffer, unsigned int offset) song = mpd_entity_get_song(entity); drtn = mpd_song_get_duration(song); totalTime += drtn; - totalSongs ++; - if(totalSongs > offset && totalSongs <= offset+MAX_ELEMENTS_PER_PAGE) { - /*Pagination*/ + entity_count ++; + if(entity_count > offset && entity_count <= offset+MAX_ELEMENTS_PER_PAGE) { + entities_returned ++; cur += json_emit_raw_str(cur, end - cur, "{\"id\":"); cur += json_emit_int(cur, end - cur, mpd_song_get_id(song)); cur += json_emit_raw_str(cur, end - cur, ",\"pos\":"); @@ -711,10 +711,6 @@ int mpd_put_queue(char *buffer, unsigned int offset) cur += json_emit_int(cur, end - cur, mpd_song_get_duration(song)); cur += json_emit_raw_str(cur, end - cur, ",\"artist\":"); cur += json_emit_quoted_str(cur, end - cur, mpd_get_artist(song)); -// cur += json_emit_raw_str(cur, end - cur, ",\"album_artist\":"); -// cur += json_emit_quoted_str(cur, end - cur, mpd_get_album_artist(song)); -// cur += json_emit_raw_str(cur, end - cur, ",\"uri\":"); -// cur += json_emit_quoted_str(cur, end - cur, mpd_song_get_uri(song)); cur += json_emit_raw_str(cur, end - cur, ",\"album\":"); cur += json_emit_quoted_str(cur, end - cur, mpd_get_album(song)); cur += json_emit_raw_str(cur, end - cur, ",\"title\":"); @@ -731,8 +727,12 @@ int mpd_put_queue(char *buffer, unsigned int offset) cur += json_emit_raw_str(cur, end - cur, "],\"totalTime\":"); cur += json_emit_int(cur, end - cur, totalTime); - cur += json_emit_raw_str(cur, end - cur, ",\"totalSongs\":"); - cur += json_emit_int(cur, end - cur, totalSongs); + cur += json_emit_raw_str(cur, end - cur, ",\"totalEntities\":"); + cur += json_emit_int(cur, end - cur, entity_count); + cur += json_emit_raw_str(cur, end - cur, ",\"offset\":"); + cur += json_emit_int(cur, end - cur, offset); + cur += json_emit_raw_str(cur, end - cur, ",\"returnedEntities\":"); + cur += json_emit_int(cur, end - cur, entities_returned); cur += json_emit_raw_str(cur, end - cur, "}"); return cur - buffer; } @@ -743,6 +743,7 @@ int mpd_put_browse(char *buffer, char *path, unsigned int offset) const char *end = buffer + MAX_SIZE; struct mpd_entity *entity; unsigned int entity_count = 0; + unsigned int entities_returned = 0; if (!mpd_send_list_meta(mpd.conn, path)) RETURN_ERROR_AND_RECOVER("mpd_send_list_meta"); @@ -752,14 +753,14 @@ int mpd_put_browse(char *buffer, char *path, unsigned int offset) while((entity = mpd_recv_entity(mpd.conn)) != NULL) { const struct mpd_song *song; const struct mpd_directory *dir; - entity_count++; + entity_count ++; if(entity_count > offset && entity_count <= offset+MAX_ELEMENTS_PER_PAGE) { switch (mpd_entity_get_type(entity)) { case MPD_ENTITY_TYPE_UNKNOWN: break; - case MPD_ENTITY_TYPE_SONG: + entities_returned ++; song = mpd_entity_get_song(entity); cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"song\",\"uri\":"); cur += json_emit_quoted_str(cur, end - cur, mpd_song_get_uri(song)); @@ -775,6 +776,7 @@ int mpd_put_browse(char *buffer, char *path, unsigned int offset) break; case MPD_ENTITY_TYPE_DIRECTORY: + entities_returned ++; dir = mpd_entity_get_directory(entity); cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"directory\",\"dir\":"); cur += json_emit_quoted_str(cur, end - cur, mpd_directory_get_path(dir)); @@ -799,6 +801,10 @@ int mpd_put_browse(char *buffer, char *path, unsigned int offset) cur--; cur += json_emit_raw_str(cur, end - cur, "],\"totalEntities\":"); cur += json_emit_int(cur, end - cur, entity_count); + cur += json_emit_raw_str(cur, end - cur, ",\"offset\":"); + cur += json_emit_int(cur, end - cur, offset); + cur += json_emit_raw_str(cur, end - cur, ",\"returnedEntities\":"); + cur += json_emit_int(cur, end - cur, entities_returned); cur += json_emit_raw_str(cur, end - cur, "}"); return cur - buffer; } @@ -809,6 +815,7 @@ int mpd_put_playlists(char *buffer, unsigned int offset) const char *end = buffer + MAX_SIZE; struct mpd_playlist *pl; unsigned int entity_count = 0; + unsigned int entities_returned = 0; if (!mpd_send_list_playlists(mpd.conn)) RETURN_ERROR_AND_RECOVER("mpd_send_lists_playlists"); @@ -816,8 +823,9 @@ int mpd_put_playlists(char *buffer, unsigned int offset) cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"playlists\",\"data\":[ "); while((pl = mpd_recv_playlist(mpd.conn)) != NULL) { - entity_count++; + entity_count ++; if(entity_count > offset && entity_count <= offset+MAX_ELEMENTS_PER_PAGE) { + entities_returned ++; cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"playlist\",\"plist\":"); cur += json_emit_quoted_str(cur, end - cur, mpd_playlist_get_path(pl)); cur += json_emit_raw_str(cur, end - cur, ",\"last_modified\":"); @@ -837,7 +845,11 @@ int mpd_put_playlists(char *buffer, unsigned int offset) cur--; cur += json_emit_raw_str(cur, end - cur, "],\"totalEntities\":"); - cur += json_emit_int(cur, end - cur, entity_count); + cur += json_emit_int(cur, end - cur, entity_count); + cur += json_emit_raw_str(cur, end - cur, ",\"offset\":"); + cur += json_emit_int(cur, end - cur, offset); + cur += json_emit_raw_str(cur, end - cur, ",\"returnedEntities\":"); + cur += json_emit_int(cur, end - cur, entities_returned); cur += json_emit_raw_str(cur, end - cur, "}"); return cur - buffer; } @@ -910,7 +922,8 @@ int mpd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char * char *cur = buffer; const char *end = buffer + MAX_SIZE; struct mpd_song *song; - unsigned long totalSongs = 0; + unsigned long entity_count = 0; + unsigned long entities_returned = 0; if(mpd_search_queue_songs(mpd.conn, false) == false) { RETURN_ERROR_AND_RECOVER("mpd_search_queue_songs"); @@ -931,8 +944,9 @@ int mpd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char * cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"queuesearch\",\"data\":[ "); while((song = mpd_recv_song(mpd.conn)) != NULL) { - totalSongs ++; - if(totalSongs > offset && totalSongs <= offset+MAX_ELEMENTS_PER_PAGE) { + entity_count ++; + if(entity_count > offset && entity_count <= offset+MAX_ELEMENTS_PER_PAGE) { + entities_returned ++; cur += json_emit_raw_str(cur, end - cur, "{\"type\":\"song\""); cur += json_emit_raw_str(cur, end - cur, ",\"id\":"); cur += json_emit_int(cur, end - cur, mpd_song_get_id(song)); @@ -954,8 +968,12 @@ int mpd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char * cur--; cur += json_emit_raw_str(cur, end - cur, "]"); - cur += json_emit_raw_str(cur, end - cur, ",\"totalSongs\":"); - cur += json_emit_int(cur, end - cur, totalSongs); + cur += json_emit_raw_str(cur, end - cur, ",\"totalEntities\":"); + cur += json_emit_int(cur, end - cur, entity_count); + cur += json_emit_raw_str(cur, end - cur, ",\"offset\":"); + cur += json_emit_int(cur, end - cur, offset); + cur += json_emit_raw_str(cur, end - cur, ",\"returnedEntities\":"); + cur += json_emit_int(cur, end - cur, entities_returned); cur += json_emit_raw_str(cur, end - cur, ",\"mpdtagtype\":"); cur += json_emit_quoted_str(cur, end - cur, mpdtagtype); cur += json_emit_raw_str(cur, end - cur, "}"); From 0039fabc508ab228e7275332e6ad769b5f011c86 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Wed, 30 May 2018 01:12:47 +0200 Subject: [PATCH 09/23] Incremental populate tables in queue, browse and search views --- htdocs/css/mpd.css | 4 ++++ htdocs/js/mpd.js | 44 ++++++++++++++++++++++++++------------------ 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/htdocs/css/mpd.css b/htdocs/css/mpd.css index acd81ee..18193e4 100644 --- a/htdocs/css/mpd.css +++ b/htdocs/css/mpd.css @@ -47,6 +47,10 @@ button { width: 200px; } +.card { + min-height:300px; +} + h1 { display: block; overflow: hidden; diff --git a/htdocs/js/mpd.js b/htdocs/js/mpd.js index 95d8dba..01fcaa7 100644 --- a/htdocs/js/mpd.js +++ b/htdocs/js/mpd.js @@ -81,7 +81,7 @@ var app = $.sammy(function() { $('#cardQueue').removeClass('hide'); $('#navQueue').addClass('active'); } - $('#queueList > tbody').empty(); + //$('#queueList > tbody').empty(); if (searchstr.length >= 3) { socket.send('MPD_API_SEARCH_QUEUE,' + mpdtag + ','+pagination+',' + searchstr); } @@ -123,7 +123,7 @@ var app = $.sammy(function() { $('#cardBrowse').removeClass('hide'); $('#cardBrowseFilesystem').removeClass('hide'); $('#cardBrowseNavFilesystem').addClass('active'); - $('#browseFilesystemList > tbody').empty(); + //$('#browseFilesystemList > tbody').empty(); $('#browseBreadcrumb').empty().append("
  • root
  • "); socket.send('MPD_API_GET_BROWSE,'+pagination+','+(browsepath ? browsepath : "/")); // Don't add all songs from root @@ -177,9 +177,10 @@ var app = $.sammy(function() { $('#cardSearch').removeClass('hide'); $('#navSearch').addClass('active'); } - $('#searchList > tbody').empty(); if (searchstr.length >= 3) { socket.send('MPD_API_SEARCH,' + mpdtag + ','+pagination+',' + searchstr); + } else { + $('#searchList > tbody').empty(); } }); @@ -285,21 +286,27 @@ function webSocketConnect() { $('#panel-heading-queue').append(' – ' + beautifyDuration(obj.totalTime)); } - $('#queueList > tbody').empty(); + //$('#queueList > tbody').empty(); var nrItems=0; + var tr=document.getElementById(current_app+'List').getElementsByTagName('tbody')[0].getElementsByTagName('tr'); for (var song in obj.data) { nrItems++; var minutes = Math.floor(obj.data[song].duration / 60); var seconds = obj.data[song].duration - minutes * 60; - $('#queueList > tbody').append( - "" + (obj.data[song].pos + 1) + "" + + var row="" + (obj.data[song].pos + 1) + "" + ""+ obj.data[song].title +"" + ""+ obj.data[song].artist +"" + ""+ obj.data[song].album +"" + ""+ minutes + ":" + (seconds < 10 ? '0' : '') + seconds + - ""); + ""; + if (nrItems < tr.length) { $(tr[nrItems-1]).replaceWith(row); } + else { $('#'+current_app+'List > tbody').append(row); } } + for (var i=tr.length;i>nrItems;i--) { + $(tr[tr.length-1]).remove(); + } + if (obj.type == 'queuesearch' && nrItems == 0) { $('#queueList > tbody').append( "error_outline" + @@ -400,7 +407,7 @@ function webSocketConnect() { } break; case 'search': - $('#searchList > tbody').empty(); + //$('#searchList > tbody').empty(); $('#panel-heading-search').text(obj.totalEntities + ' Songs found'); case 'browse': if(current_app !== 'browseFilesystem' && current_app !== 'search') @@ -411,34 +418,35 @@ function webSocketConnect() { * URI from NFD to NFC, breaking our link with MPD. */ var nrItems=0; + var tr=document.getElementById(current_app+'List').getElementsByTagName('tbody')[0].getElementsByTagName('tr'); for (var item in obj.data) { nrItems++; + var row=''; switch(obj.data[item].type) { case 'directory': - $('#'+current_app+'List > tbody').append( - '' + + row='' + 'folder_open' + '' + basename(obj.data[item].dir) + '' + - '' - ); + ''; break; case 'song': var minutes = Math.floor(obj.data[item].duration / 60); var seconds = obj.data[item].duration - minutes * 60; - - $('#'+current_app+'List > tbody').append( - '' + + row='' + 'music_note' + '' + obj.data[item].title + '' + '' + obj.data[item].artist + '' + '' + obj.data[item].album + '' + '' + minutes + ':' + (seconds < 10 ? '0' : '') + seconds + - '' - ); + ''; break; } + if (nrItems < tr.length) { $(tr[nrItems-1]).replaceWith(row); } + else { $('#'+current_app+'List > tbody').append(row); } + } + for (var i=tr.length;i>nrItems;i--) { + $(tr[tr.length-1]).remove(); } - setPagination(obj.totalEntities); if (current_app == 'search') From 060aeff3ffda35744639efb7b75b21d5c30b3e66 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Wed, 30 May 2018 01:28:36 +0200 Subject: [PATCH 10/23] Fixing incremental table change --- htdocs/js/mpd.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/htdocs/js/mpd.js b/htdocs/js/mpd.js index 01fcaa7..bf26736 100644 --- a/htdocs/js/mpd.js +++ b/htdocs/js/mpd.js @@ -300,11 +300,11 @@ function webSocketConnect() { ""+ obj.data[song].album +"" + ""+ minutes + ":" + (seconds < 10 ? '0' : '') + seconds + ""; - if (nrItems < tr.length) { $(tr[nrItems-1]).replaceWith(row); } + if (nrItems <= tr.length) { $(tr[nrItems-1]).replaceWith(row); } else { $('#'+current_app+'List > tbody').append(row); } } for (var i=tr.length;i>nrItems;i--) { - $(tr[tr.length-1]).remove(); + $(tr[tr.length-1]).remove(); } if (obj.type == 'queuesearch' && nrItems == 0) { @@ -401,7 +401,7 @@ function webSocketConnect() { if (nrItems == 0) { $('#'+current_app+'List > tbody').append( "error_outline" + - "No results" + + "No playlists found." + "" ); } @@ -441,11 +441,11 @@ function webSocketConnect() { ''; break; } - if (nrItems < tr.length) { $(tr[nrItems-1]).replaceWith(row); } + if (nrItems <= tr.length) { $(tr[nrItems-1]).replaceWith(row); } else { $('#'+current_app+'List > tbody').append(row); } } for (var i=tr.length;i>nrItems;i--) { - $(tr[tr.length-1]).remove(); + $(tr[tr.length-1]).remove(); } setPagination(obj.totalEntities); From 7d5accdb92f0a8f62a09735f4926eebd983560a0 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Thu, 31 May 2018 21:23:57 +0100 Subject: [PATCH 11/23] Added Replaygain to settings Improved coverimage display --- htdocs/assets/coverimage-notavailable.png | Bin 0 -> 10105 bytes ...tpstream.png => coverimage_httpstream.png} | Bin htdocs/index.html | 10 +++ htdocs/js/mpd.js | 83 ++++++++++-------- src/mpd_client.c | 37 +++++++- src/mpd_client.h | 3 +- 6 files changed, 92 insertions(+), 41 deletions(-) create mode 100644 htdocs/assets/coverimage-notavailable.png rename htdocs/assets/{httpstream.png => coverimage_httpstream.png} (100%) diff --git a/htdocs/assets/coverimage-notavailable.png b/htdocs/assets/coverimage-notavailable.png new file mode 100644 index 0000000000000000000000000000000000000000..d5a9f61a84abfe604d9640ea3cd6f3e2702e3b51 GIT binary patch literal 10105 zcmd72hc{gB8}_?LH#(ztqDMrF-bM`(MDIid5sa3J-l9f{NbuE5qD74wbp}B~^bjSC z2%|>tgR}k4e{j}1>wT>mdl`H0dCGq7`?@~&6L0uXn*z!L1pt8JzK+Ht@VI;PCLscU zE1Ryr1rG$i@cSkt;7>5glQ{5iQiP7BF91;X-n=32MJQRp51IWmE&PnVp86pjeVhR# z5_!kX)7{s}5#fBt%f}`ApCStYZ~^x<)J$IHZ2v~SgssdH?FPMQnm*Jep(e5Yh@_@Z zEh*sPO0YfT;^81G3chz39ysY_-s=MxQ4^vgNdzBJeI>0P*;&0W`!lBLXWX*D$Exl(Nc0J zax&ZqHR{hqo{< zwt5L#0%umXtRUqt2LJ{}_bL_4iu?{@5m9EjK5|T2Vo1TP=l6+oj2p->LBv(10PE6Xv4t!CCIcVjQ`NX zAmy@JbIe}j%@bHS!HW77i1#u*bEhT*JqJzS4Z ztKzzj=mme&>D%gC1Pt!K>H6dS&cm7rahU$>HVmUxq~8IHM|R=UnrgU0!rxNhk zo!x@LF**&o&D`zmrfkIhW4VHJP2}_!GALu8d%>I45yGt$ z4?_4+5?krD$d;Mw*~Be#{;`#kyG1~3ys$8Hxq;^^{*OTxQ&=J8q|c(9h0o_CS`G|e zIp;nur<3Qo8Fi)rZA@SMUBuIW+Q(ge-R`0!gXdNX;1`!IPrNyw)B+DI?RSuB47!~c zRJ!*%sD_Q1V>Vcl9ZhS=b7L~^9~9o-n8zG_A^VKjh#o^=Imp!gt5^wJA{sn`ihfU6 zqp}{eG;kpQiNh)bSytN2A`ULPO>AF^Lm5`l#?;7(dP4!f=cBFd&H=J#Lo0}tC$Gv| zr{eMEfr>R)gMCY}T5LvWGu_usJNVM9=8sjnX63|3No(G|G{^}_2b0-1)5}82QI&-1 zcKR=qA+dw7+^S@}3kEV}?h_ot%@j+4Ez1+ZH1CKD)#1VmA-f zo?I>>PPaZfR#qMr$ko9MM%PmJf3?2p89Y-QuotH)Wk^c%cnLw}hT)aku|?%jJ&jTP zqnkF4Uw+V)feQ@K(}Xa*HY)frZ#r5^1$FaAhVp;TV@WLXe{DTfa6O){tU480h+upY z-Oye=0o)on|FATH-w1~}q&}_=ft4&Q!>yYBq$CXlx69-R97lhtD8VY$G5(iwqV+XD z8*a6rxT~dMFR3tO+87LTpz+Hk;_}=$;1+fNeZ~BlQ9pddk;2#XN9>4Q%$DY|wwG6GjbJ z^kah(l~5t7kUJOmDOu1f@uF){HO_bJ(kk%l1wppKnHaFt&)`a9N#@2f+;Q7zLaGg70XwMP8 zM=wr?EyTPgq%{)=lcN1Znf30UPSCoH5xco^5VHjK$n6lq;%%I#y+vU_`pmlJsbev* zI`~*nzCMZiX-y#36YyJhZ|x%@SBb~bd0#7b)L*A%g>S55SZ?RH=;oN|BX4ZJ>hQ%$ zoK=ib1&z$MX1wq-Wge&Z;BB{tT=6!R;JDz4VF=|^Hfwu1Ikm?BjFJwdb)r^e~76ho! z_Lkt4qOpr^>Ig^`DJPN-TnpVo|51%?sU)njYm(ZapE#JO#DP6*n_Qcl@tkaN9lbyr1~*zucVf9{8{ z-`uP~!uT|xHVcnD9FJUeKD$ujqTtp02G45AFfvcQd=v!c5EmrbvVQTLk9V~#a-B{V zJTGnon<2m4Ti1|4ssQeG@TFhoraJk;%Kcyza^GYiZ940DQx>p~$(w!Tnp6 zAN=VKW6#OI z#=^uyi9LusFoAm3jI!`mYHI_yOU#jPe{=(Xs|KcAkx(d;__vpx#wd({(4sdxJW%Lyt81WM(s*UZVqqm+CX1o^x#FMYANH zo5*xMi}+x}a2h7ahlXC#XP+K!%-_aF-ob4U!a|SW#u;N=k39_wXuKg+SJW)wWoK2c z)E$1k?M>D7-Cn`2-vm(wbHB34TC0o|p?7;bm_b>fV{w18#BI(PVcl9K368XPK5Di} z37z$7F;wT3qyE@v6TNbsGCQ2uFx=?qDfPK%Kq6j@r&Qg`7UovlJEwX-<+@TCNj*hYy?%}xg@5`* zMVen}n>NWjd)#sJY>a(8qL-^vCOl;Wuen(A=oVlg4zUiFwbW=&&zYv^Ph|(!ff)D* zhn7+d(_IiWHZ*3E1Y8sR;TDJ5tWwW+5jw7>nPg~u_mqaVuA2L+~n+v{2+;YI}z)SERTsI&IS zx&8+^0*=yE>O-%z!3htxd$(i52d)!!2<4x~)t;+qgUGqf31T$^`+MvX>L;nANM_-d zkgq$7J?=j;YI)8I0s8`Q1sZKT)r37N(zQ~qtI7S0dzM??slPi6)k=9=xg6aL17VXA8|Vmv=R&We98@1#uIia-m^vo|cGDQeuh z-tkm{M$Nbgm@aU=6e%j18Ee{(ANaJB0meJNXG70j=WO{~Iu=({{}*vmgN?%7F`|R& zGb8!n-9k6ug1l)Tgvtnwv~F>|*p~!jli_N0+Jy;%m6?f&gV5Z5O8cr$pFG9Gas%*t4V?O1r{?2_9 z(?QjI)RExXlBh`@&UR9w;+M$>=rC$1~~=<4j_X;7)0iciLHk(V8HINt4WUMB(F zd!3?za^O6>9-uy($qhs~nz|lfpiJtS9UokKJ|Q^fc-2frsX#f=?wnE}u^GO$G8Dzn z>wk4pwW07H#-j9yQb3U1J_lQFv&@#UR=3wy-gPFSwMtsEkIhEIxT3J;Z4pvu-pnHBE2f+$B63(we#h4axsdoc`h z#;Da#W!9*H?5?>>}B% z?-)|>=5;L8Z$0)3BpRDm1n!FZk;r4U*H@`hyKv&I8k0JLsH6w^$|Rtx{EZp2{*eSO zL~vQsDqT{$F+mZ=4lrnik*zDKZ?NTu|cU>6fDSXjimPdO5auCj%wSB0d93)h!(4{&vm9nIG z)m3sQHXLt$>#nfqgAf7Gu#+>1wUvAm=v~Gs%Tju1&NMj-E?$1z+OhX^1BpZCI4!Yy ziiqQyH{s2Yv*%9<&iUsmkzb%iK}`h(54$sZtj=v3iqP7`vd6}bYs$1>@+-K%K@Kk5 z$+~iG`}9A-b}yj@-S5*NCo{Z=JRAiGHZY<;D128mgkcW{gmHd`Z2>QYLcqo*=}%=d!c$Hi5vU>pZKWKt@KjhbC2N9 zxa@s40A+6((Jf8(Ny%$VnkdGT7XX?<7E!5n#&6}NzS+`vpLqb`-_ydR*4pCqL-EgDncZq$IU{AUiMxzjzFv1cw$0y|A0#58)3taz_W))NWsBCD5`)OGwb1 z-%&ry7ctn1YUBDuDgvb_HsM`u?S}w%?r2TOJwH~o%8rnnh-LWC_}tTN3uN#@0l*q) z7Y`;B0QlFw48u6wU=@&HU14u*q{40$W+m;PHQq-)j)i^rlX-Rl&G;mq7k}vDSR}Kr zXIh>3hH=r!pf}~KATm&||mUDLYcGoRE`Z-EBG4}99$FF0S+dWNo(OOi4yVjpB zhv%;YL9nx%&KTpNkQCvo;Jtrl6w!ZJMX6ssrcgKOy-lNV_FvyZCD-|3gabvY;Tmsu ztKHxmHwTIc|2&0r(3)u2ga%nOPcX@LQ5|=MMqbwBV`ST_$_L$!SVU3PIpn9px&Q#C zTln0+&waBE4y_OI`7FKumi-3+U32j{Cp*!L+vK>b??FIJ>XYmb{;mz`uEWOhxRn&{ z^-d~ut?sQKIKa|?5)XS4?XQp+87$^Er?duTpQ5%+^W; zQ2lyEv75e%Y6?@j9pxD*Uq^StZ!T+oI4*j2FcD7fu@^}7M;dKnK&P6EBo8gE@*XsF zE4;r&5Y0^s5Zh}C&C4-kg7e-?mpAmPG5;ijCUGo$Rs_)uV8hgfm*`lKya{~dqjZ3Q zqt=}k#a4( z;m)J_rX!2i?%~U&Vp@MqPXItyrXvM6l2?WdGAefA2KfU3%yk~gUL1S;%$sglkNI`~ zz;Po;6ee$c6fLL1)_+{!`dhDd+tzydoSp@Ld{Kb{*nQ8q^eg8u(7htQe1t$FC7%q0 zo({0*jaZzgfbg-GDmr5X?SEbn-bmQ(*8T(r#yUON>|P&mNd;%km`M(m(qYXouoUr?0AsZz7tQpMwVY_TAz+0T#hHfDs==Z<6i ztz#aj_lI3agOKlZ*C$UGj1K^Td}?AxU(sd#9JS1L^hRtIIX)QFr>Fz2f?h<0_OtMy zYYVrrU?2xgDTvms#h-VD5CdhW+d(g@&H%u-D?UOgfZ}=3^uDx3j6S1Z3%mGZqOJ#L z*PYeJIRT?jK8PvIo9~><*09oKEG9P{bae@~0|g15QK!~+@a$OS8t))D6S#mM z)^Ms#u2}}Zyu3qv&EZ6m8pkA#&u*_oVB8hY?ZFF6Vs!Y(0$}8W2tcz=c&}QLd#}2g zrNJ&_+7VQ0`>1m{NM=0nRP3Qht)sB(hL_7o}ya;6#o zGssg3K34FESTwyf57-!a^Q=BG6B6AA0D>3(a$U=Rf;rw@?%M+q(|mvS6`t8Fct4k! z4~QtnH}!*p9p9YKlUGhsu{lX+EGl{y3n7rFN^=Q;YJG%I%R&c&i#RD4pY|NvWC&N?g_>doE{#jsd(w%WUBjoT%6y8cbgxzb}?J0In6W zC+Ppakc+dimJ1Gc-u1O;qtW8 z@4C;k&Upi4*WvIge82>Eg+JWxF9MQ6^iD$rNczF4jdo`!LLAlOB*xUHt9tkXyH(Em z&+?Tbx@+vogU4D*X+vG)zzaZ0D$^6X<^n_%XU5Sq|NPtpEPpIss%hZ%y;6PWamkeT zM{>7~4eW&G!e?#6+6}i(kD(7|*pvnW-OimJ6wR&h?w=z(*vp_#9sP=$GdFPbiykKs zuF5&`+w1kLR$11Q+frC>ZsfNR9IGs7%1#dq#L0hasiw`-0~jG?_FrUEMIcVPQcVA8 z?ZENUiR3+5>5e=bNap|mTc^aBJNU&%0wepGT@{ly@#~<11?+xPPFU`3e6PxbA1=&_ z@55}44@!UL9l)G>S}!GkQF&;7D>-rBo+RZM`>><-ju#TgwCF&xp7ea#oh-lwO1km3VN7+;v*)Gy9j_K0 zP4dp^KrT?BWWRrzEql=CmlJ%PV5LwNj!NFO(N2N8L>4_`dM=Al7V}ZbN}9OtgjkZr zbX%vJ2eGU5Jb1s_s*Uju<@}Bev)nw0csOK#n(xnp_k2SkBFv`gvc5GaOYVk2=G&X0 z&VtmX*MI8-405h$^JpezSvnDG*hY>><}kK9y8nU7XT0~>DQoCOgJ~Lw*jq&B^P|5b zYhp$m34zRWTViJoc+z&|T>4(hkEmzLiB07H^{L#sSeam%BSC}I#yqdHD?>)85qv5J0#-^iy+(q%Y^S#aw3wX8Tpe{T;3S znS|8q^&XwP4sr*WPSzRZq2iSFuC%JD3GMYS<9D`RK?ZwnS9{SJkZvI`x||Ldft=I5 zqiF*vXV|&ytC5^V8e_`+_%FNQ`S~ASn$2ocot8`gyTTMh_+F|D{ZwYxw;Y%(VhAYn zG3SD@90K^!t8gIfJbiobEJcz zw#jjWUQvZu)6oxyszZ$_MZ24fgrcmt7`pMXzcmf{_V;@AxQ-sfep1F>db(7__5+xc zJWdDNfE6V{tG3J?X{CO1BfL4U#ha$QOMki6mJ|*G$g6;mbFQ+-a}W|g!`wCv(~jE= z-V=5jho`aXowpe%b@deAx`f~@J5Rsz$W?|!8APfS(pwc{{E52^$bWy$eIeaPS@#W} z9FWudr#&kY1}~iAKJFu|{by%el~H4+=H;iQGg8D;mZVYqG%>@D!atb!3R^aIP2FB8 zU#*mwkqH0F$to5&-0HzRbSVGa8#PIoUEncCr;72hT~DW4lEsODF~yW!pDj4q z$JJO&t?iml7Op?AC{dPSKj}bueb%&ceZitkBa@%QDu4UqQyK^4f;X(nGOoog zfLuHPbu?frPnI)67Gv72=qeFjf#xB;XVXy0Uw&}LPKwn&9!%S4ZrRtyTB)d>Bjf&1oFv`9pYqj)fjl&YKI|f}zAd8Il7I4C%usA%6tsLHEYt z%-Px)62IW>yG-wiN;>HZ5+A&3mTIbXnKNk)+`VhTaiogQX$c+~ZbLJR<%7GaI~z?YJ7SdYqa`pVSU zXL$oPhHDPv{sv9oHqxe$oRSf_B_n=b)vAG3*1lFb7MXc&X1*{o0m6#cW)FYAo?``gEwM(+CX`F&yTS^R|*#8oSN%{v? zl8?(BB8O?18=IzBVDfa2`E3zB8nRTSQ5$8SN-Z;{s8%7<#6JyB7D0HvIlm|9_jIj| z0vo0*^Ib0vEMVQlM}FeIp=tEwj{L2KGyWS5bHozpk!2bVDZtbVoc8p`IX0`>N{?6Y zzn=-q<@9k$(84v$2Guz64@b3b+@14Z_4-9+w!^iAvkPr+fV;P^C6}CMp4$;S|KuY! zSh{b5J4-8D5xJTj;(S!`cnK?|=f@jGbl5e|tLD_b@m&BdL6k zE#s&48wn)6g*t)aSC`Yp=@%aIuz&Lh{*(8Ht6%i;11AOS8Ut8m2g(no)aiL~Qd}lK?x4VQ z>Z1}MxQpMT$~<{ymDHm|Af~Rs3PDlc1z1{nY*O?=J~qY)b#Qdlzbfjb1-NUfP09DQeQ@j)WVg3ir}F{^Nd@ ztYzxQYC&WG>8Uu~+)Svr##aSnAh?xbbm{BwTJmg4=O;X{XY61eNk#OBQ7i1>j5N_$ zgZOF%a|^;T&3GrnzZFJ zevb)778{{WG?D}qn!YPfFq7S5t{1=L!F?r}#QLj2&SHOkExcD7g&G&;tUH6GAA4B< zTnNe+{}d~CKQV6P*c#>jisu|0Itvk;JNNGJpZ>6gO#ywi+g&I_-@Y7f`Hsi{J>S&p8O2-k$1K3)DGMko_OC8RP@-{_31P|`8yBFa zxdbiU>#Yod=))cPeh{95o`-74g#eSpEl4=H7d|n)aHc*X`Hcdj2__rS@Mq#MAOr zCrwZGOW9O0w3#5`gjIz_hjoyd4dH*1R0o+G7;V4|h)e#yQ~kGjd3Z$Q?4MYw>AR#G zbG;=U(-lIW!qco|Jo`E>0}Ko9LS)MFp**0XZ(XGYN#gI?ec>V{{O9=Jh#B^@SGd8U?Y7 z&Q$Y;)WhpfH4L@*;f3NT5PXc-U9a3LWFjP+qNR$qBKESh%gx|X|2Vpa&U%ch)qSbw zdv=x9H^qbhK*xP-OTLbTyZisLjavU7Hjf=&J5hz04+T_KfZODN` +
    +
    +
    Replaygain
    +
    + +
    diff --git a/htdocs/js/mpd.js b/htdocs/js/mpd.js index bf26736..7636cfb 100644 --- a/htdocs/js/mpd.js +++ b/htdocs/js/mpd.js @@ -45,7 +45,6 @@ var app = $.sammy(function() { $('#cardQueue').addClass('hide'); $('#cardBrowse').addClass('hide'); $('#cardSearch').addClass('hide'); - $('.pagination').addClass('hide'); $('#searchqueue > input').val(''); $('#cardBrowsePlaylists').addClass('hide'); $('#cardBrowseDatabase').addClass('hide'); @@ -81,7 +80,6 @@ var app = $.sammy(function() { $('#cardQueue').removeClass('hide'); $('#navQueue').addClass('active'); } - //$('#queueList > tbody').empty(); if (searchstr.length >= 3) { socket.send('MPD_API_SEARCH_QUEUE,' + mpdtag + ','+pagination+',' + searchstr); } @@ -123,7 +121,6 @@ var app = $.sammy(function() { $('#cardBrowse').removeClass('hide'); $('#cardBrowseFilesystem').removeClass('hide'); $('#cardBrowseNavFilesystem').addClass('active'); - //$('#browseFilesystemList > tbody').empty(); $('#browseBreadcrumb').empty().append("
  • root
  • "); socket.send('MPD_API_GET_BROWSE,'+pagination+','+(browsepath ? browsepath : "/")); // Don't add all songs from root @@ -252,14 +249,13 @@ function webSocketConnect() { try { socket.onopen = function() { console.log("connected"); - showNotification('Connected to myMPD','','','success'); - $('#modalConnectionError').modal('hide'); - app.run(); /* emit request for mympd settings */ socket.send('MPD_API_GET_SETTINGS'); /* emit initial request for output names */ socket.send('MPD_API_GET_OUTPUTS'); - + showNotification('Connected to myMPD','','','success'); + $('#modalConnectionError').modal('hide'); + app.run(); } socket.onmessage = function got_packet(msg) { @@ -278,7 +274,6 @@ function webSocketConnect() { if(current_app !== 'queue') break; $('#panel-heading-queue').empty(); - if (obj.totalEntities > 0) { $('#panel-heading-queue').text(obj.totalEntities+' Songs'); } @@ -286,7 +281,6 @@ function webSocketConnect() { $('#panel-heading-queue').append(' – ' + beautifyDuration(obj.totalTime)); } - //$('#queueList > tbody').empty(); var nrItems=0; var tr=document.getElementById(current_app+'List').getElementsByTagName('tbody')[0].getElementsByTagName('tr'); for (var song in obj.data) { @@ -407,7 +401,6 @@ function webSocketConnect() { } break; case 'search': - //$('#searchList > tbody').empty(); $('#panel-heading-search').text(obj.totalEntities + ' Songs found'); case 'browse': if(current_app !== 'browseFilesystem' && current_app !== 'search') @@ -577,7 +570,6 @@ function webSocketConnect() { break; case 'disconnected': showNotification('myMPD lost connection to MPD','','','danger'); - break; case 'update_queue': if(current_app === 'queue') { @@ -631,6 +623,8 @@ function webSocketConnect() { $('#btnrepeat').removeClass('btn-secondary').addClass("btn-success") else $('#btnrepeat').removeClass("btn-success").addClass("btn-secondary"); + + $("#selectReplaygain").val(obj.data.replaygain); setLocalStream(obj.data.mpdhost,obj.data.streamport); coverImageFile=obj.data.coverimage; @@ -823,55 +817,68 @@ function toggleBtn(btn) { } $('#btnrandom').on('click', function (e) { - socket.send("MPD_API_TOGGLE_RANDOM," + ($(this).hasClass('btn-success') ? 0 : 1)); toggleBtn(this); }); $('#btnconsume').on('click', function (e) { - socket.send("MPD_API_TOGGLE_CONSUME," + ($(this).hasClass('btn-success') ? 0 : 1)); toggleBtn(this); }); $('#btnsingle').on('click', function (e) { - socket.send("MPD_API_TOGGLE_SINGLE," + ($(this).hasClass('btn-success') ? 0 : 1)); - toggleBtn(this); + toggleBtn(this); }); $('#btnrepeat').on('click', function (e) { - socket.send("MPD_API_TOGGLE_REPEAT," + ($(this).hasClass('btn-success') ? 0 : 1)); toggleBtn(this); }); function confirmSettings() { - var value=parseInt($('#inputCrossfade').val()); - if (!isNaN(value)) { + var formOK=true; + if (!$('#inputCrossfade').is(':disabled')) { + var value=parseInt($('#inputCrossfade').val()); + if (!isNaN(value)) { $('#inputCrossfade').val(value); - socket.send("MPD_API_SET_CROSSFADE," + value); - } else { + } else { $('#inputCrossfade').popover({"content":"Must be a number","trigger":"manual"}); $('#inputCrossfade').popover('show'); $('#inputCrossfade').focus(); - return; + formOK=false; + } } - value=parseFloat($('#inputMixrampdb').val()); - if (!isNaN(value)) { + if (!$('#inputMixrampdb').is(':disabled')) { + value=parseFloat($('#inputMixrampdb').val()); + if (!isNaN(value)) { $('#inputMixrampdb').val(value); - socket.send("MPD_API_SET_MIXRAMPDB," + value); - } else { + } else { $('#inputMixrampdb').popover({"content":"Must be a number","trigger":"manual"}); $('#inputMixrampdb').popover('show'); $('#inputMixrampdb').focus(); - return; - } - value=parseFloat($('#inputMixrampdelay').val()); - if (!isNaN(value)) { + formOK=false; + } + } + if (!$('#inputMixrampdelay').is(':disabled')) { + value=parseFloat($('#inputMixrampdelay').val()); + if (!isNaN(value)) { $('#inputMixrampdelay').val(value); - socket.send("MPD_API_SET_MIXRAMPDELAY," + value); - } else { + } else { $('#inputMixrampdelay').popover({"content":"Must be a number","trigger":"manual"}); $('#inputMixrampdelay').popover('show'); $('#inputMixrampdelay').focus(); - return; + formOK=false; + } + } + if (formOK == true) { + socket.send("MPD_API_TOGGLE_CONSUME," + ($('#btnconsume').hasClass('btn-success') ? 1 : 0)); + socket.send("MPD_API_TOGGLE_RANDOM," + ($('#btnrandom').hasClass('btn-success') ? 1 : 0)); + socket.send("MPD_API_TOGGLE_SINGLE," + ($('#btnsingle').hasClass('btn-success') ? 1 : 0)); + socket.send("MPD_API_TOGGLE_REPEAT," + ($('#btnrepeat').hasClass('btn-success') ? 1 : 0)); + socket.send("MPD_API_SET_REPLAYGAIN," + $('#selectReplaygain').val()); + if (!$('#inputCrossfade').is(':disabled')) + socket.send("MPD_API_SET_CROSSFADE," + $('#inputCrossfade').val()); + if (!$('#inputMixrampdb').is(':disabled')) + socket.send("MPD_API_SET_MIXRAMPDB," + $('#inputMixrampdb').val()); + if (!$('#inputMixrampdelay').is(':disabled')) + socket.send("MPD_API_SET_MIXRAMPDELAY," + $('#inputMixrampdelay').val()); + $('#settings').modal('hide'); } - $('#settings').modal('hide'); } function toggleoutput(button, id) { @@ -1061,12 +1068,14 @@ function songChange(title, artist, album, uri) { if (typeof uri != 'undefined' && uri.length > 0) { var coverImg=''; - if (uri.indexOf('http://') == 0) { - coverImg='/assets/httpstream.png'; - } else { + if (uri.indexOf('http://') == 0 || uri.indexOf('https://') == 0 ) { + coverImg='/assets/coverimage-httpstream.png'; + } else if (coverImageFile != '') { coverImg='/library/'+uri.replace(/\/[^\/]+$/,'\/'+coverImageFile); + } else { + coverImg='/assets/coverimage-notavailable.png'; } - $('#album-cover').css('backgroundImage','url("'+coverImg+'")'); + $('#album-cover').css('backgroundImage','url("'+coverImg+'"),url("/assets/coverimage-notavailable.png")'); } if(typeof artist != 'undefined' && artist.length > 0 && artist != '-') { textNotification += artist; diff --git a/src/mpd_client.c b/src/mpd_client.c index ed2c085..76a0235 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -197,7 +197,7 @@ out_browse: if((token = strtok(NULL, ",")) == NULL) goto out_add_track; - free(p_charbuf); + free(p_charbuf); p_charbuf = strdup(c->content); mpd_run_add(mpd.conn, get_arg1(p_charbuf)); out_add_track: @@ -337,6 +337,24 @@ out_rm_playlist: case MPD_API_GET_STATS: n = mympd_get_stats(mpd.buf); break; + case MPD_API_SET_REPLAYGAIN: + p_charbuf = strdup(c->content); + if(strcmp(strtok(p_charbuf, ","), "MPD_API_SET_REPLAYGAIN")) + goto out_set_replaygain; + + if((token = strtok(NULL, ",")) == NULL) + goto out_set_replaygain; + + free(p_charbuf); + p_charbuf = strdup(c->content); + mpd_send_command(mpd.conn, "replay_gain_mode", get_arg1(p_charbuf), NULL); + struct mpd_pair *pair; + while ((pair = mpd_recv_pair(mpd.conn)) != NULL) { + mpd_return_pair(mpd.conn, pair); + } +out_set_replaygain: + free(p_charbuf); + break; } if(mpd.conn_state == MPD_CONNECTED && mpd_connection_get_error(mpd.conn) != MPD_ERROR_SUCCESS) @@ -549,6 +567,7 @@ char* mpd_get_year(struct mpd_song const *song) int mpd_put_state(char *buffer, int *current_song_id, int *next_song_id, unsigned *queue_version) { struct mpd_status *status; + const struct mpd_audio_format *audioformat; int len; status = mpd_run_status(mpd.conn); @@ -557,11 +576,13 @@ int mpd_put_state(char *buffer, int *current_song_id, int *next_song_id, unsign mpd.conn_state = MPD_FAILURE; return 0; } + audioformat = mpd_status_get_audio_format(status); len = snprintf(buffer, MAX_SIZE, "{\"type\":\"state\", \"data\":{" "\"state\":%d, \"volume\":%d, \"songpos\": %d, \"elapsedTime\": %d, " "\"totalTime\":%d, \"currentsongid\": %d, \"kbitrate\": %d, " + "\"audioformat\": { \"sample_rate\": %d, \"bits\": %d, \"channels\": %d}, " "\"queue_length\": %d, \"nextsongpos\": %d" "}}", mpd_status_get_state(status), @@ -571,6 +592,7 @@ int mpd_put_state(char *buffer, int *current_song_id, int *next_song_id, unsign mpd_status_get_total_time(status), mpd_status_get_song_id(status), mpd_status_get_kbit_rate(status), + audioformat->sample_rate, audioformat->bits, audioformat->channels, mpd_status_get_queue_length(status), mpd_status_get_next_song_pos(status) ); @@ -586,6 +608,7 @@ int mympd_put_settings(char *buffer) { struct mpd_status *status; int len; + char *replaygain; status = mpd_run_status(mpd.conn); if (!status) { @@ -594,11 +617,18 @@ int mympd_put_settings(char *buffer) return 0; } + mpd_send_command(mpd.conn, "replay_gain_status", NULL); + struct mpd_pair *pair; + while ((pair = mpd_recv_pair(mpd.conn)) != NULL) { + replaygain=strdup(pair->value); + mpd_return_pair(mpd.conn, pair); + } + len = snprintf(buffer, MAX_SIZE, "{\"type\":\"settings\", \"data\":{" "\"repeat\":%d, \"single\":%d, \"crossfade\":%d, \"consume\":%d, \"random\":%d, " "\"mixrampdb\": %lf, \"mixrampdelay\": %lf, \"mpdhost\" : \"%s\", \"mpdport\": \"%d\", \"passwort_set\": %s, " - "\"streamport\": \"%d\",\"coverimage\": \"%s\", \"max_elements_per_page\": %d" + "\"streamport\": \"%d\",\"coverimage\": \"%s\", \"max_elements_per_page\": %d, \"replaygain\": \"%s\"" "}}", mpd_status_get_repeat(status), mpd_status_get_single(status), @@ -610,7 +640,8 @@ int mympd_put_settings(char *buffer) mpd.host, mpd.port, mpd.password ? "true" : "false", streamport, coverimage, - MAX_ELEMENTS_PER_PAGE + MAX_ELEMENTS_PER_PAGE, + replaygain ); mpd_status_free(status); return len; diff --git a/src/mpd_client.h b/src/mpd_client.h index 78a83f6..d662a0a 100644 --- a/src/mpd_client.h +++ b/src/mpd_client.h @@ -78,7 +78,8 @@ X(MPD_API_SET_MIXRAMPDB) \ X(MPD_API_SET_MIXRAMPDELAY) \ X(MPD_API_GET_PLAYLISTS) \ - X(MPD_API_RM_PLAYLIST) + X(MPD_API_RM_PLAYLIST) \ + X(MPD_API_SET_REPLAYGAIN) enum mpd_cmd_ids { MPD_CMDS(GEN_ENUM) From c5505c728d00cc677de3a8802f21762f751c6af2 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Thu, 31 May 2018 21:42:39 +0100 Subject: [PATCH 12/23] Fix queue pagination --- htdocs/css/mpd.css | 2 -- htdocs/js/mpd.js | 6 +++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/htdocs/css/mpd.css b/htdocs/css/mpd.css index 18193e4..77d71dc 100644 --- a/htdocs/css/mpd.css +++ b/htdocs/css/mpd.css @@ -21,12 +21,10 @@ body { width: 104px; } - button { overflow: hidden; } - #browseBreadcrumb { overflow: auto; white-space: nowrap; diff --git a/htdocs/js/mpd.js b/htdocs/js/mpd.js index 7636cfb..a2ace6f 100644 --- a/htdocs/js/mpd.js +++ b/htdocs/js/mpd.js @@ -1000,7 +1000,11 @@ function gotoPage(x,element,event) { if ($('#searchqueuestr').val().length >=3) { doQueueSearch(); } else { - app.setLocation('#/queue/'+pagination); + var mpdtag='Any Tag'; + $('#searchqueuetag > button').each(function() { + if ($(this).hasClass('btn-success')) { mpdtag=$(this).text(); } + }); + app.setLocation('#/queue/'+pagination+'/'+mpdtag+'/'); } break; case "search": From efb3ef87be53b480efe3d8721cf79621c44e4c15 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Fri, 1 Jun 2018 00:05:49 +0100 Subject: [PATCH 13/23] New function: AddAllFromSearch Styling cleanups --- htdocs/css/mpd.css | 2 +- htdocs/index.html | 38 ++++++++++++++++++--------- htdocs/js/mpd.js | 64 ++++++++++++++++++++++++++++++++++------------ src/mpd_client.c | 62 +++++++++++++++++++++++++++++++++++++++++--- src/mpd_client.h | 2 ++ 5 files changed, 135 insertions(+), 33 deletions(-) diff --git a/htdocs/css/mpd.css b/htdocs/css/mpd.css index 77d71dc..faaa5a6 100644 --- a/htdocs/css/mpd.css +++ b/htdocs/css/mpd.css @@ -123,7 +123,7 @@ tbody { word-wrap: normal; white-space: nowrap; direction: ltr; - + vertical-align: top; /* Support for all WebKit browsers. */ -webkit-font-smoothing: antialiased; /* Support for Safari and Chrome. */ diff --git a/htdocs/index.html b/htdocs/index.html index 15eee35..8a5db9f 100644 --- a/htdocs/index.html +++ b/htdocs/index.html @@ -54,7 +54,7 @@