1
0
mirror of https://github.com/SuperBFG7/ympd synced 2024-12-26 02:50:26 +00:00

Feat: last played songs card

This commit is contained in:
jcorporation 2018-11-12 23:48:29 +00:00
parent 7fdd0fe560
commit ba187b17ce
11 changed files with 291 additions and 30 deletions

View File

@ -72,6 +72,7 @@ post_upgrade() {
[ -f /var/lib/mympd/state/colsBrowsePlaylistsDetail ] || echo -n '["Pos","Title","Artist","Album","Duration"]' > /var/lib/mympd/state/colsBrowsePlaylistsDetail [ -f /var/lib/mympd/state/colsBrowsePlaylistsDetail ] || echo -n '["Pos","Title","Artist","Album","Duration"]' > /var/lib/mympd/state/colsBrowsePlaylistsDetail
[ -f /var/lib/mympd/state/colsQueue ] || echo -n '["Pos","Title","Artist","Album","Duration"]' > /var/lib/mympd/state/colsQueue [ -f /var/lib/mympd/state/colsQueue ] || echo -n '["Pos","Title","Artist","Album","Duration"]' > /var/lib/mympd/state/colsQueue
[ -f /var/lib/mympd/state/colsSearch ] || echo -n '["Title","Artist","Album","Duration"]' > /var/lib/mympd/state/colsSearch [ -f /var/lib/mympd/state/colsSearch ] || echo -n '["Title","Artist","Album","Duration"]' > /var/lib/mympd/state/colsSearch
[ -f /var/lib/mympd/state/colsLastPlayed ] || echo -n '["Pos","Title","Artist","Album","Duration"]' > /var/lib/mympd/state/colsLastPlayed
# fix ownership of /var/lib/mympd # fix ownership of /var/lib/mympd
echo "INFO: Fixing ownership of /var/lib/mympd" echo "INFO: Fixing ownership of /var/lib/mympd"

View File

@ -87,6 +87,7 @@ done
[ -f /var/lib/mympd/state/colsBrowsePlaylistsDetail ] || echo -n '["Pos","Title","Artist","Album","Duration"]' > /var/lib/mympd/state/colsBrowsePlaylistsDetail [ -f /var/lib/mympd/state/colsBrowsePlaylistsDetail ] || echo -n '["Pos","Title","Artist","Album","Duration"]' > /var/lib/mympd/state/colsBrowsePlaylistsDetail
[ -f /var/lib/mympd/state/colsQueue ] || echo -n '["Pos","Title","Artist","Album","Duration"]' > /var/lib/mympd/state/colsQueue [ -f /var/lib/mympd/state/colsQueue ] || echo -n '["Pos","Title","Artist","Album","Duration"]' > /var/lib/mympd/state/colsQueue
[ -f /var/lib/mympd/state/colsSearch ] || echo -n '["Title","Artist","Album","Duration"]' > /var/lib/mympd/state/colsSearch [ -f /var/lib/mympd/state/colsSearch ] || echo -n '["Title","Artist","Album","Duration"]' > /var/lib/mympd/state/colsSearch
[ -f /var/lib/mympd/state/colsLastPlayed ] || echo -n '["Pos","Title","Artist","Album","Duration"]' > /var/lib/mympd/state/colsLastPlayed
echo "Fixing ownership of /var/lib/mympd" echo "Fixing ownership of /var/lib/mympd"
chown -R mympd.mympd /var/lib/mympd chown -R mympd.mympd /var/lib/mympd

View File

@ -54,3 +54,6 @@ syscmds = false
#Elements per page for pagination, max: 400 #Elements per page for pagination, max: 400
max_elements_per_page = 100 max_elements_per_page = 100
#Number of songs keep in last played list
last_played_count = 20

1
debian/postinst vendored
View File

@ -66,6 +66,7 @@ fi
[ -f /var/lib/mympd/state/colsPlayback ] || echo -n '["Artist","Album","Genre"]' > /var/lib/mympd/state/colsPlayback [ -f /var/lib/mympd/state/colsPlayback ] || echo -n '["Artist","Album","Genre"]' > /var/lib/mympd/state/colsPlayback
[ -f /var/lib/mympd/state/colsBrowsePlaylistsDetail ] || echo -n '["Pos","Title","Artist","Album","Duration"]' > /var/lib/mympd/state/colsBrowsePlaylistsDetail [ -f /var/lib/mympd/state/colsBrowsePlaylistsDetail ] || echo -n '["Pos","Title","Artist","Album","Duration"]' > /var/lib/mympd/state/colsBrowsePlaylistsDetail
[ -f /var/lib/mympd/state/colsQueue ] || echo -n '["Pos","Title","Artist","Album","Duration"]' > /var/lib/mympd/state/colsQueue [ -f /var/lib/mympd/state/colsQueue ] || echo -n '["Pos","Title","Artist","Album","Duration"]' > /var/lib/mympd/state/colsQueue
[ -f /var/lib/mympd/state/colsLastPlayed ] || echo -n '["Pos","Title","Artist","Album","Duration"]' > /var/lib/mympd/state/colsLastPlayed
[ -f /var/lib/mympd/state/colsSearch ] || echo -n '["Title","Artist","Album","Duration"]' > /var/lib/mympd/state/colsSearch [ -f /var/lib/mympd/state/colsSearch ] || echo -n '["Title","Artist","Album","Duration"]' > /var/lib/mympd/state/colsSearch
echo "Fixing ownership of /var/lib/mympd" echo "Fixing ownership of /var/lib/mympd"

View File

@ -199,6 +199,62 @@
</div> </div>
</div> </div>
<div class="card hide" id="cardLastPlayed">
<div class="card-header">Last Played Songs<span id="panel-heading-last-played" class="text pull-right"></span></div>
<div class="card-body">
<div class="btn-toolbar card-toolbar">
<div id="LastPlayedPaginationTop" class="btn-group mr-2 hide">
<button data-href='{"cmd": "gotoPage", "options": ["prev"]}' id="LastPlayedPaginationTopPrev" title="Previous Page" type="button" class="btn btn-secondary">&laquo;</button>
<div class="input-group-append">
<button id="LastPlayedPaginationTopPage" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">1 / 1</button>
<div class="dropdown-menu bg-dark px-2 pages" id="LastPlayedPaginationTopPages">
</div>
</div>
<button data-href='{"cmd": "gotoPage", "options": ["next"]}' id="LastPlayedPaginationTopNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">&raquo;</button>
</div>
<div class="btn-group mr-2 featTags">
<button id="LastPlayedColsBtn" class="btn btn-secondary dropdown-toggle material-icons" type="button" data-toggle="dropdown">settings</button>
<div class="dropdown-menu bg-dark px-2" id="LastPlayedColsDropdown"><form></form>
<button data-href='{"cmd": "saveCols", "options": ["LastPlayed"]}' class="btn btn-success btn-block btn-sm mt-2">Apply</button>
</div>
</div>
</div>
<div class="table-responsive-md">
<table id="LastPlayedList" class="table table-hover table-sm">
<thead>
<tr>
<th>#</th>
<th>Title</th>
<th>Artist</th>
<th>Album</th>
<th>Duration</th>
<th></th>
</tr>
</thead>
<tbody class="clickable">
</tbody>
</table>
</div>
<div class="btn-toolbar hide" id="LastPlayedButtonsBottom">
<div class="btn-group mr-2">
<button type="button" class="btn btn-secondary material-icons" data-href='{"cmd": "scrollTo", "options: [0]}' title="To top">
keyboard_arrow_up
</button>
</div>
<div id="LastPlayedPaginationBottom" class="btn-group mr-2 dropup">
<button data-href='{"cmd": "gotoPage", "options": ["prev"]}' id="LastPlayedPaginationBottomPrev" title="Previous Page" type="button" class="btn btn-secondary">&laquo;</button>
<div class="input-group-append">
<button id="LastPlayedPaginationBottomPage" class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">1 / 1</button>
<div class="dropdown-menu bg-dark px-2 pages" id="LastPlayedPaginationBottomPages">
</div>
</div>
<button data-href='{"cmd": "gotoPage", "options": ["next"]}' id="LastPlayedPaginationBottomNext" title="Next Page" type="button" class="btn btn-secondary input-group-append">&raquo;</button>
</div>
</div>
</div>
</div>
<div class="card hide" id="cardBrowse"> <div class="card hide" id="cardBrowse">
<div class="card-header" id="panel-heading-browse"> <div class="card-header" id="panel-heading-browse">
<ul class="nav nav-tabs card-header-tabs"> <ul class="nav nav-tabs card-header-tabs">

View File

@ -37,9 +37,10 @@ var dragEl;
var playlistEl; var playlistEl;
var app = {}; var app = {};
app.apps = { "Playback": { "state": "0/-/", "scrollPos": 0 }, app.apps = { "Playback": { "state": "0/-/", "scrollPos": 0 },
"Queue": { "state": "0/any/", "scrollPos": 0 }, "Queue": { "state": "0/any/", "scrollPos": 0 },
"Browse": { "LastPlayed": { "state": "0/any/", "scrollPos": 0 },
"Browse": {
"active": "Database", "active": "Database",
"tabs": { "Filesystem": { "state": "0/-/", "scrollPos": 0 }, "tabs": { "Filesystem": { "state": "0/-/", "scrollPos": 0 },
"Playlists": { "Playlists": {
@ -96,7 +97,7 @@ var modalSaveSmartPlaylist = new Modal(document.getElementById('modalSaveSmartPl
var modalDeletePlaylist = new Modal(document.getElementById('modalDeletePlaylist')); var modalDeletePlaylist = new Modal(document.getElementById('modalDeletePlaylist'));
var modalHelp = new Modal(document.getElementById('modalHelp')); var modalHelp = new Modal(document.getElementById('modalHelp'));
var dropdownMainMenu;// = new Dropdown(document.getElementById('mainMenu')); var dropdownMainMenu;
var dropdownVolumeMenu = new Dropdown(document.getElementById('volumeMenu')); var dropdownVolumeMenu = new Dropdown(document.getElementById('volumeMenu'));
var collapseDBupdate = new Collapse(document.getElementById('navDBupdate')); var collapseDBupdate = new Collapse(document.getElementById('navDBupdate'));
@ -111,6 +112,7 @@ function appPrepare(scrollPos) {
document.getElementById('cardQueue').classList.add('hide'); document.getElementById('cardQueue').classList.add('hide');
document.getElementById('cardBrowse').classList.add('hide'); document.getElementById('cardBrowse').classList.add('hide');
document.getElementById('cardSearch').classList.add('hide'); document.getElementById('cardSearch').classList.add('hide');
document.getElementById('cardLastPlayed').classList.add('hide');
for (var i = 0; i < domCache.panelHeadingBrowseLen; i++) { for (var i = 0; i < domCache.panelHeadingBrowseLen; i++) {
domCache.panelHeadingBrowse[i].classList.remove('active'); domCache.panelHeadingBrowse[i].classList.remove('active');
} }
@ -119,7 +121,8 @@ function appPrepare(scrollPos) {
document.getElementById('cardBrowseFilesystem').classList.add('hide'); document.getElementById('cardBrowseFilesystem').classList.add('hide');
//show active card + nav //show active card + nav
document.getElementById('card' + app.current.app).classList.remove('hide'); document.getElementById('card' + app.current.app).classList.remove('hide');
document.getElementById('nav' + app.current.app).classList.add('active'); if (document.getElementById('nav' + app.current.app))
document.getElementById('nav' + app.current.app).classList.add('active');
if (app.current.tab != undefined) { if (app.current.tab != undefined) {
document.getElementById('card' + app.current.app + app.current.tab).classList.remove('hide'); document.getElementById('card' + app.current.app + app.current.tab).classList.remove('hide');
document.getElementById('card' + app.current.app + 'Nav' + app.current.tab).classList.add('active'); document.getElementById('card' + app.current.app + 'Nav' + app.current.tab).classList.add('active');
@ -203,6 +206,9 @@ function appRoute() {
selectTag('searchqueuetags', 'searchqueuetagsdesc', app.current.filter); selectTag('searchqueuetags', 'searchqueuetagsdesc', app.current.filter);
getQueue(); getQueue();
} }
else if (app.current.app == 'LastPlayed') {
sendAPI({"cmd": "MPD_API_QUEUE_LAST_PLAYED", "data": {"offset": app.current.page}}, parseLastPlayed);
}
else if (app.current.app == 'Browse' && app.current.tab == 'Playlists' && app.current.view == 'All') { else if (app.current.app == 'Browse' && app.current.tab == 'Playlists' && app.current.view == 'All') {
sendAPI({"cmd": "MPD_API_PLAYLIST_LIST", "data": {"offset": app.current.page, "filter": app.current.filter}}, parsePlaylists); sendAPI({"cmd": "MPD_API_PLAYLIST_LIST", "data": {"offset": app.current.page, "filter": app.current.filter}}, parsePlaylists);
doSetFilterLetter('BrowsePlaylistsFilter'); doSetFilterLetter('BrowsePlaylistsFilter');
@ -264,10 +270,13 @@ function appRoute() {
if (searchstrEl.value == '' && app.current.search != '') if (searchstrEl.value == '' && app.current.search != '')
searchstrEl.value = app.current.search; searchstrEl.value = app.current.search;
if (app.last.app != app.current.app) { if (app.last.app != app.current.app) {
if (app.current.search != '') if (app.current.search != '') {
var colspan = settings['cols' + app.current.app].length;
colspan--;
document.getElementById('SearchList').getElementsByTagName('tbody')[0].innerHTML= document.getElementById('SearchList').getElementsByTagName('tbody')[0].innerHTML=
'<tr><td><span class="material-icons">search</span></td>' + '<tr><td><span class="material-icons">search</span></td>' +
'<td colspan="5">Searching...</td></tr>'; '<td colspan="' + colspan + '">Searching...</td></tr>';
}
} }
if (app.current.search.length >= 2) { if (app.current.search.length >= 2) {
@ -455,10 +464,14 @@ function appInit() {
document.getElementById('QueueList').addEventListener('click', function(event) { document.getElementById('QueueList').addEventListener('click', function(event) {
if (event.target.nodeName == 'TD') if (event.target.nodeName == 'TD')
sendAPI({"cmd": "MPD_API_PLAYER_PLAY_TRACK","data": {"track": event.target.parentNode.getAttribute('data-trackid')}}); sendAPI({"cmd": "MPD_API_PLAYER_PLAY_TRACK","data": {"track": event.target.parentNode.getAttribute('data-trackid')}});
else if (event.target.nodeName == 'A') { else if (event.target.nodeName == 'A')
showMenu(event.target, event); showMenu(event.target, event);
}
}, false); }, false);
document.getElementById('LastPlayedList').addEventListener('click', function(event) {
if (event.target.nodeName == 'A')
showMenu(event.target, event);
}, false);
document.getElementById('BrowseFilesystemList').addEventListener('click', function(event) { document.getElementById('BrowseFilesystemList').addEventListener('click', function(event) {
if (event.target.nodeName == 'TD') { if (event.target.nodeName == 'TD') {
@ -566,7 +579,7 @@ function appInit() {
}, false); }, false);
var dropdowns = ['QueueColsDropdown', 'BrowseFilesystemColsDropdown', 'SearchColsDropdown', 'BrowsePlaylistsDetailColsDropdown', var dropdowns = ['QueueColsDropdown', 'BrowseFilesystemColsDropdown', 'SearchColsDropdown', 'BrowsePlaylistsDetailColsDropdown',
'BrowseDatabaseColsDropdown', 'PlaybackColsDropdown']; 'BrowseDatabaseColsDropdown', 'PlaybackColsDropdown', 'LastPlayedColsDropdown'];
for (var i = 0; i < dropdowns.length; i++) { for (var i = 0; i < dropdowns.length; i++) {
document.getElementById(dropdowns[i]).addEventListener('click', function(event) { document.getElementById(dropdowns[i]).addEventListener('click', function(event) {
if (event.target.nodeName == 'INPUT') if (event.target.nodeName == 'INPUT')
@ -601,6 +614,7 @@ function appInit() {
dragAndDropTable('QueueList'); dragAndDropTable('QueueList');
dragAndDropTable('BrowsePlaylistsDetailList'); dragAndDropTable('BrowsePlaylistsDetailList');
dragAndDropTableHeader('Queue'); dragAndDropTableHeader('Queue');
dragAndDropTableHeader('LastPlayed');
dragAndDropTableHeader('Search'); dragAndDropTableHeader('Search');
dragAndDropTableHeader('BrowseFilesystem'); dragAndDropTableHeader('BrowseFilesystem');
dragAndDropTableHeader('BrowsePlaylistsDetail'); dragAndDropTableHeader('BrowsePlaylistsDetail');
@ -964,7 +978,7 @@ function filterCols(x) {
if (settings.featTags == false) if (settings.featTags == false)
tags.push('Title'); tags.push('Title');
tags.push('Duration'); tags.push('Duration');
if (x == 'colsQueue' || x == 'colsBrowsePlaylistsDetail') if (x == 'colsQueue' || x == 'colsBrowsePlaylistsDetail' || x == 'colsLastPlayed')
tags.push('Pos'); tags.push('Pos');
else if (x == 'colsBrowseFilesystem') else if (x == 'colsBrowseFilesystem')
tags.push('Type'); tags.push('Type');
@ -1046,6 +1060,7 @@ function parseSettings(obj) {
app.apps.Search.state = '0/filename/'; app.apps.Search.state = '0/filename/';
app.apps.Queue.state = '0/filename/'; app.apps.Queue.state = '0/filename/';
settings.colsQueue = ["Pos", "Title", "Duration"]; settings.colsQueue = ["Pos", "Title", "Duration"];
settings.colsLastPlayed = ["Pos", "Title", "Duration"];
settings.colsSearch = ["Title", "Duration"]; settings.colsSearch = ["Title", "Duration"];
settings.colsBrowseFilesystem = ["Type", "Title", "Duration"]; settings.colsBrowseFilesystem = ["Type", "Title", "Duration"];
settings.colsBrowseDatabase = ["Track", "Title", "Duration"]; settings.colsBrowseDatabase = ["Track", "Title", "Duration"];
@ -1100,6 +1115,7 @@ function parseSettings(obj) {
settings.browsetags.sort(); settings.browsetags.sort();
filterCols('colsSearch'); filterCols('colsSearch');
filterCols('colsQueue'); filterCols('colsQueue');
filterCols('colsLastPlayed');
filterCols('colsBrowsePlaylistsDetail'); filterCols('colsBrowsePlaylistsDetail');
filterCols('colsBrowseFilesystem'); filterCols('colsBrowseFilesystem');
filterCols('colsBrowseDatabase'); filterCols('colsBrowseDatabase');
@ -1144,12 +1160,15 @@ function parseSettings(obj) {
setCols('Queue'); setCols('Queue');
setCols('Search'); setCols('Search');
setCols('LastPlayed');
setCols('BrowseFilesystem'); setCols('BrowseFilesystem');
setCols('BrowsePlaylistsDetail'); setCols('BrowsePlaylistsDetail');
setCols('BrowseDatabase', '.tblAlbumTitles'); setCols('BrowseDatabase', '.tblAlbumTitles');
setCols('Playback'); setCols('Playback');
if (app.current.app == 'Queue') if (app.current.app == 'Queue')
getQueue(); getQueue();
else if (app.current.app == 'LastPlayed')
appRoute();
else if (app.current.app == 'Search') else if (app.current.app == 'Search')
appRoute(); appRoute();
else if (app.current.app == 'Browse' && app.current.tab == 'Filesystem') else if (app.current.app == 'Browse' && app.current.tab == 'Filesystem')
@ -1166,7 +1185,7 @@ function setCols(table, className) {
if (settings.featTags == false) if (settings.featTags == false)
tags.push('Title'); tags.push('Title');
tags.push('Duration'); tags.push('Duration');
if (table == 'Queue' || table == 'BrowsePlaylistsDetail') if (table == 'Queue' || table == 'BrowsePlaylistsDetail' || table == 'LastPlayed')
tags.push('Pos'); tags.push('Pos');
if (table == 'BrowseFilesystem') if (table == 'BrowseFilesystem')
tags.push('Type'); tags.push('Type');
@ -1399,6 +1418,9 @@ function parseState(obj) {
for (var i = 0; i < pb.length; i++) for (var i = 0; i < pb.length; i++)
pb[i].innerText = ''; pb[i].innerText = '';
} }
if (app.current.app == 'LastPlayed')
sendAPI({"cmd": "MPD_API_QUEUE_LAST_PLAYED", "data": {"offset": app.current.page}}, parseLastPlayed);
lastState = obj; lastState = obj;
} }
@ -1475,17 +1497,65 @@ function parseQueue(obj) {
tr[i].remove(); tr[i].remove();
} }
var colspan = settings['cols' + app.current.app].length;
colspan--;
if (obj.type == 'queuesearch' && nrItems == 0) if (obj.type == 'queuesearch' && nrItems == 0)
tbody.innerHTML = '<tr><td><span class="material-icons">error_outline</span></td>' + tbody.innerHTML = '<tr><td><span class="material-icons">error_outline</span></td>' +
'<td colspan="5">No results, please refine your search!</td></tr>'; '<td colspan="' + colspan + '">No results, please refine your search!</td></tr>';
else if (obj.type == 'queue' && nrItems == 0) else if (obj.type == 'queue' && nrItems == 0)
tbody.innerHTML = '<tr><td><span class="material-icons">error_outline</span></td>' + tbody.innerHTML = '<tr><td><span class="material-icons">error_outline</span></td>' +
'<td colspan="5">Empty queue</td></tr>'; '<td colspan="' + colspan + '">Empty queue</td></tr>';
setPagination(obj.totalEntities); setPagination(obj.totalEntities);
document.getElementById('QueueList').classList.remove('opacity05'); document.getElementById('QueueList').classList.remove('opacity05');
} }
function parseLastPlayed(obj) {
if (app.current.app !== 'LastPlayed')
return;
document.getElementById('panel-heading-last-played').innerText = obj.totalEntities + ' Songs';
var nrItems = obj.data.length;
var table = document.getElementById(app.current.app + 'List');
table.setAttribute('data-version', obj.queueVersion);
var tbody = table.getElementsByTagName('tbody')[0];
var tr = tbody.getElementsByTagName('tr');
for (var i = 0; i < nrItems; i++) {
var minutes = Math.floor(obj.data[i].Duration / 60);
var seconds = obj.data[i].Duration - minutes * 60;
obj.data[i].Duration = minutes + ':' + (seconds < 10 ? '0' : '') + seconds;
var row = document.createElement('tr');
row.setAttribute('data-songpos', (obj.data[i].Pos + 1));
row.setAttribute('data-uri', obj.data[i].uri);
var tds = '';
for (var c = 0; c < settings.colsLastPlayed.length; c++) {
tds += '<td data-col="' + settings.colsLastPlayed[c] + '">' + obj.data[i][settings.colsLastPlayed[c]] + '</td>';
}
tds += '<td data-col="Action"><a href="#" class="material-icons color-darkgrey">playlist_add</a></td>';
row.innerHTML = tds;
if (i < tr.length)
tr[i].replaceWith(row);
else
tbody.append(row);
}
var trLen = tr.length - 1;
for (var i = trLen; i >= nrItems; i --) {
tr[i].remove();
}
var colspan = settings['cols' + app.current.app].length;
colspan--;
if (nrItems == 0)
tbody.innerHTML = '<tr><td><span class="material-icons">error_outline</span></td>' +
'<td colspan="' + colspan + '">Empty list</td></tr>';
setPagination(obj.totalEntities);
document.getElementById('LastPlayedList').classList.remove('opacity05');
}
function parseSearch(obj) { function parseSearch(obj) {
if (app.current.app !== 'Search') if (app.current.app !== 'Search')
return; return;
@ -1574,7 +1644,7 @@ function parseFilesystem(obj) {
if (nrItems == 0) if (nrItems == 0)
tbody.innerHTML = '<tr><td><span class="material-icons">error_outline</span></td>' + tbody.innerHTML = '<tr><td><span class="material-icons">error_outline</span></td>' +
'<td colspan="5">No results</td></tr>'; '<td colspan="' + colspan + '">No results</td></tr>';
document.getElementById(app.current.app + (app.current.tab==undefined ? '' : app.current.tab) + 'List').classList.remove('opacity05'); document.getElementById(app.current.app + (app.current.tab==undefined ? '' : app.current.tab) + 'List').classList.remove('opacity05');
} }
@ -1671,13 +1741,16 @@ function parsePlaylists(obj) {
setPagination(obj.totalEntities); setPagination(obj.totalEntities);
if (nrItems == 0) if (nrItems == 0) {
var colspan = settings['cols' + list].length;
colspan--;
if (app.current.view == 'All') if (app.current.view == 'All')
tbody.innerHTML = '<tr><td><span class="material-icons">error_outline</span></td>' + tbody.innerHTML = '<tr><td><span class="material-icons">error_outline</span></td>' +
'<td colspan="5">No playlists found.</td></tr>'; '<td colspan="' + colspan + '">No playlists found.</td></tr>';
else else
tbody.innerHTML = '<tr><td><span class="material-icons">error_outline</span></td>' + tbody.innerHTML = '<tr><td><span class="material-icons">error_outline</span></td>' +
'<td colspan="5">Empty playlist.</td></tr>'; '<td colspan="' + colspan + '">Empty playlist.</td></tr>';
}
document.getElementById(app.current.app + app.current.tab + app.current.view + 'List').classList.remove('opacity05'); document.getElementById(app.current.app + app.current.tab + app.current.view + 'List').classList.remove('opacity05');
} }

View File

@ -140,6 +140,34 @@ int list_push(struct list *l, const char *data, int value) {
return 0; return 0;
} }
struct node *list_node_extract(struct list *l, unsigned idx) {
if (l->list == NULL) { return NULL; }
struct node *current = l->list, **previous = &l->list;
for (; idx > 0; idx--) {
if (current->next == NULL)
return NULL;
previous = &current->next;
current = current->next;
}
/* set the previous node's 'next' value to the current
* nodes next value */
*previous = current->next;
/* null out this node's next value since it's not part of
* a list anymore */
current->next = NULL;
l->length--;
return current;
}
int list_shift(struct list *l, unsigned idx) {
struct node * extracted = list_node_extract(l, idx);
if (extracted == NULL)
return -1;
free(extracted->data);
free(extracted);
return 0;
}
int list_free(struct list *l) { int list_free(struct list *l) {
struct node *current = l->list, *tmp = NULL; struct node *current = l->list, *tmp = NULL;
while (current != NULL) { while (current != NULL) {

View File

@ -13,6 +13,8 @@ struct list {
int list_init(struct list *l); int list_init(struct list *l);
int list_push(struct list *l, const char *data, int value); int list_push(struct list *l, const char *data, int value);
int list_shift(struct list *l, unsigned idx);
struct node *list_node_extract(struct list *l, unsigned idx);
int list_replace(struct list *l, int pos, const char *data, int value); int list_replace(struct list *l, int pos, const char *data, int value);
int list_free(struct list *l); int list_free(struct list *l);
int list_get_value(const struct list *l, const char *data); int list_get_value(const struct list *l, const char *data);

View File

@ -142,6 +142,10 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
free(mympd_state.colsPlayback); free(mympd_state.colsPlayback);
mympd_state.colsPlayback = strdup(cols); mympd_state.colsPlayback = strdup(cols);
} }
else if (strcmp(p_charbuf1,"colsLastPlayed")==0) {
free(mympd_state.colsLastPlayed);
mympd_state.colsLastPlayed = strdup(cols);
}
mympd_state_set(p_charbuf1, cols); mympd_state_set(p_charbuf1, cols);
free(p_charbuf1); free(p_charbuf1);
} }
@ -396,6 +400,12 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
n = mympd_put_queue(mpd.buf, uint_buf1, &mpd.queue_version, &mpd.queue_length); n = mympd_put_queue(mpd.buf, uint_buf1, &mpd.queue_version, &mpd.queue_length);
} }
break; break;
case MPD_API_QUEUE_LAST_PLAYED:
je = json_scanf(msg.p, msg.len, "{data: {offset: %u}}", &uint_buf1);
if (je == 1) {
n = mympd_put_last_played_songs(mpd.buf, uint_buf1);
}
break;
case MPD_API_PLAYER_CURRENT_SONG: case MPD_API_PLAYER_CURRENT_SONG:
n = mympd_put_current_song(mpd.buf); n = mympd_put_current_song(mpd.buf);
break; break;
@ -688,10 +698,14 @@ void mympd_parse_idle(struct mg_mgr *s, int idle_bitmask) {
break; break;
case MPD_IDLE_PLAYER: case MPD_IDLE_PLAYER:
len = mympd_put_state(mpd.buf, &mpd.song_id, &mpd.next_song_id, &mpd.last_song_id, &mpd.queue_version, &mpd.queue_length); len = mympd_put_state(mpd.buf, &mpd.song_id, &mpd.next_song_id, &mpd.last_song_id, &mpd.queue_version, &mpd.queue_length);
if (config.stickers && mpd.song_id != mpd.last_song_id && mpd.last_update_sticker_song_id != mpd.song_id) { if (mpd.song_id != mpd.last_song_id) {
mympd_count_song_id(mpd.song_id, "playCount", 1); if (mpd.last_last_played_id != mpd.song_id)
mympd_last_played_song_id(mpd.song_id); mympd_last_played_list(mpd.song_id);
mpd.last_update_sticker_song_id = mpd.song_id; if (config.stickers && mpd.last_update_sticker_song_id != mpd.song_id) {
mympd_count_song_id(mpd.song_id, "playCount", 1);
mympd_last_played_song_id(mpd.song_id);
mpd.last_update_sticker_song_id = mpd.song_id;
}
} }
break; break;
case MPD_IDLE_MIXER: case MPD_IDLE_MIXER:
@ -998,6 +1012,22 @@ void mympd_like_song_uri(const char *uri, int value) {
LOG_ERROR_AND_RECOVER("mpd_send_sticker_set"); LOG_ERROR_AND_RECOVER("mpd_send_sticker_set");
} }
void mympd_last_played_list(int song_id) {
struct mpd_song *song;
if (song_id > -1) {
song = mpd_run_get_queue_song_id(mpd.conn, song_id);
if (song) {
list_push(&last_played, mpd_song_get_uri(song), 1);
mpd.last_last_played_id = song_id;
mpd_song_free(song);
if (last_played.length > config.last_played_count) {
list_shift(&last_played, 0);
}
}
}
}
void mympd_last_played_song_id(int song_id) { void mympd_last_played_song_id(int song_id) {
struct mpd_song *song; struct mpd_song *song;
if (song_id > -1) { if (song_id > -1) {
@ -1441,13 +1471,16 @@ int mympd_put_settings(char *buffer) {
} }
len += json_printf(&out, "]"); len += json_printf(&out, "]");
} }
len += json_printf(&out, ", colsQueue: %s", mympd_state.colsQueue); len += json_printf(&out, ", colsQueue: %s, colsSearch: %s, colsBrowseDatabase: %s, colsBrowsePlaylistsDetail: %s, "
len += json_printf(&out, ", colsSearch: %s", mympd_state.colsSearch); "colsBrowseFilesystem: %s, colsPlayback: %s, colsLastPlayed: %s}}",
len += json_printf(&out, ", colsBrowseDatabase: %s", mympd_state.colsBrowseDatabase); mympd_state.colsQueue,
len += json_printf(&out, ", colsBrowsePlaylistsDetail: %s", mympd_state.colsBrowsePlaylistsDetail); mympd_state.colsSearch,
len += json_printf(&out, ", colsBrowseFilesystem: %s", mympd_state.colsBrowseFilesystem); mympd_state.colsBrowseDatabase,
len += json_printf(&out, ", colsPlayback: %s", mympd_state.colsPlayback); mympd_state.colsBrowsePlaylistsDetail,
len += json_printf(&out, "}}"); mympd_state.colsBrowseFilesystem,
mympd_state.colsPlayback,
mympd_state.colsLastPlayed
);
CHECK_RETURN_LEN(); CHECK_RETURN_LEN();
return len; return len;
@ -1616,6 +1649,51 @@ int mympd_put_songdetails(char *buffer, char *uri) {
return len; return len;
} }
int mympd_put_last_played_songs(char *buffer, unsigned int offset) {
const struct mpd_song *song;
struct mpd_entity *entity;
int len;
unsigned int entity_count = 0;
unsigned int entities_returned = 0;
struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE);
len = json_printf(&out, "{type: last_played_songs, data: [");
struct node *current = last_played.list;
while (current != NULL) {
entity_count++;
if (entity_count > offset && entity_count <= offset + config.max_elements_per_page) {
if (entities_returned++)
len += json_printf(&out, ",");
len += json_printf(&out, "{Pos: %d, ", entity_count);
if (!mpd_send_list_all_meta(mpd.conn, current->data))
RETURN_ERROR_AND_RECOVER("mpd_send_list_all_meta");
if ((entity = mpd_recv_entity(mpd.conn)) != NULL) {
song = mpd_entity_get_song(entity);
if (mpd.feat_tags == true)
PUT_SONG_TAGS();
else
PUT_MIN_SONG_TAGS();
mpd_entity_free(entity);
mpd_response_finish(mpd.conn);
}
len += json_printf(&out, "}");
}
current = current->next;
}
len += json_printf(&out, "], totalEntities: %d, offset: %d, returnedEntities: %d}",
entity_count,
offset,
entities_returned
);
CHECK_RETURN_LEN();
return len;
}
int mympd_put_queue(char *buffer, unsigned int offset, unsigned *queue_version, unsigned *queue_length) { int mympd_put_queue(char *buffer, unsigned int offset, unsigned *queue_version, unsigned *queue_length) {
struct mpd_entity *entity; struct mpd_entity *entity;
unsigned long totalTime = 0; unsigned long totalTime = 0;

View File

@ -86,6 +86,7 @@
X(MPD_API_QUEUE_ADD_PLAYLIST) \ X(MPD_API_QUEUE_ADD_PLAYLIST) \
X(MPD_API_QUEUE_REPLACE_PLAYLIST) \ X(MPD_API_QUEUE_REPLACE_PLAYLIST) \
X(MPD_API_QUEUE_SHUFFLE) \ X(MPD_API_QUEUE_SHUFFLE) \
X(MPD_API_QUEUE_LAST_PLAYED) \
X(MPD_API_PLAYLIST_CLEAR) \ X(MPD_API_PLAYLIST_CLEAR) \
X(MPD_API_PLAYLIST_RENAME) \ X(MPD_API_PLAYLIST_RENAME) \
X(MPD_API_PLAYLIST_MOVE_TRACK) \ X(MPD_API_PLAYLIST_MOVE_TRACK) \
@ -157,6 +158,7 @@ struct t_mpd {
unsigned queue_version; unsigned queue_version;
unsigned queue_length; unsigned queue_length;
int last_update_sticker_song_id; int last_update_sticker_song_id;
int last_last_played_id;
// Features // Features
const unsigned* protocol; const unsigned* protocol;
@ -171,7 +173,7 @@ struct list mpd_tags;
struct list mympd_tags; struct list mympd_tags;
struct list mympd_searchtags; struct list mympd_searchtags;
struct list mympd_browsetags; struct list mympd_browsetags;
struct list last_played;
struct list syscmds; struct list syscmds;
typedef struct { typedef struct {
@ -199,6 +201,7 @@ typedef struct {
bool localplayer; bool localplayer;
long streamport; long streamport;
const char *streamurl; const char *streamurl;
unsigned long last_played_count;
} t_config; } t_config;
t_config config; t_config config;
@ -222,6 +225,7 @@ typedef struct {
char *colsBrowsePlaylistsDetail; char *colsBrowsePlaylistsDetail;
char *colsBrowseFilesystem; char *colsBrowseFilesystem;
char *colsPlayback; char *colsPlayback;
char *colsLastPlayed;
} t_mympd_state; } t_mympd_state;
t_mympd_state mympd_state; t_mympd_state mympd_state;
@ -241,6 +245,7 @@ void mympd_like_song_uri(const char *uri, int value);
void mympd_last_played_song_uri(const char *uri); void mympd_last_played_song_uri(const char *uri);
void mympd_last_played_song_id(int song_id); void mympd_last_played_song_id(int song_id);
void mympd_get_sticker(const char *uri, t_sticker *sticker); void mympd_get_sticker(const char *uri, t_sticker *sticker);
void mympd_last_played_list(int song_id);
void mympd_jukebox(); void mympd_jukebox();
bool mympd_state_get(char *name, char *value); bool mympd_state_get(char *name, char *value);
bool mympd_state_set(const char *name, const char *value); bool mympd_state_set(const char *name, const char *value);
@ -270,6 +275,7 @@ int mympd_put_songs_in_album(char *buffer, char *album, char *search, char *tag)
int mympd_put_playlists(char *buffer, unsigned int offset, char *filter); int mympd_put_playlists(char *buffer, unsigned int offset, char *filter);
int mympd_put_playlist_list(char *buffer, char *uri, unsigned int offset, char *filter); int mympd_put_playlist_list(char *buffer, char *uri, unsigned int offset, char *filter);
int mympd_put_songdetails(char *buffer, char *uri); int mympd_put_songdetails(char *buffer, char *uri);
int mympd_put_last_played_songs(char *buffer, unsigned int offset);
int mympd_queue_crop(char *buffer); int mympd_queue_crop(char *buffer);
void mympd_disconnect(); void mympd_disconnect();
#endif #endif

View File

@ -199,6 +199,8 @@ static int inihandler(void* user, const char* section, const char* name, const c
p_config->localplayer = false; p_config->localplayer = false;
else if (MATCH("streamurl")) else if (MATCH("streamurl"))
p_config->streamurl = strdup(value); p_config->streamurl = strdup(value);
else if (MATCH("last_played_count"))
p_config->last_played_count = strtol(value, &crap, 10);
else else
return 0; /* unknown section/name, error */ return 0; /* unknown section/name, error */
@ -318,6 +320,13 @@ void read_statefiles() {
mympd_state.colsPlayback = strdup("[\"Artist\",\"Album\",\"Genre\"]"); mympd_state.colsPlayback = strdup("[\"Artist\",\"Album\",\"Genre\"]");
mympd_state_set("colsPlayback", mympd_state.colsPlayback); mympd_state_set("colsPlayback", mympd_state.colsPlayback);
} }
if (mympd_state_get("colsLastPlayed", value))
mympd_state.colsLastPlayed = strdup(value);
else {
mympd_state.colsLastPlayed = strdup("[\"Pos\",\"Title\",\"Artist\",\"Album\",\"Duration\"]");
mympd_state_set("colsLastPlayed", mympd_state.colsLastPlayed);
}
} }
bool testdir(char *name, char *dirname) { bool testdir(char *name, char *dirname) {
@ -363,6 +372,7 @@ int main(int argc, char **argv) {
config.browsetaglist = "Artist,Album,AlbumArtist,Genre,Composer,Performer"; config.browsetaglist = "Artist,Album,AlbumArtist,Genre,Composer,Performer";
config.smartpls = true; config.smartpls = true;
config.max_elements_per_page = 100; config.max_elements_per_page = 100;
config.last_played_count = 20;
char *etcdir = strdup(argv[1]); char *etcdir = strdup(argv[1]);
config.etcdir = dirname(etcdir); config.etcdir = dirname(etcdir);
config.syscmds = false; config.syscmds = false;
@ -371,6 +381,7 @@ int main(int argc, char **argv) {
mpd.timeout = 3000; mpd.timeout = 3000;
mpd.last_update_sticker_song_id = -1; mpd.last_update_sticker_song_id = -1;
mpd.last_song_id = -1; mpd.last_song_id = -1;
mpd.last_last_played_id = -1;
mpd.feat_library = false; mpd.feat_library = false;
if (argc == 2) { if (argc == 2) {
@ -487,6 +498,7 @@ int main(int argc, char **argv) {
list_init(&mpd_tags); list_init(&mpd_tags);
list_init(&mympd_tags); list_init(&mympd_tags);
list_init(&last_played);
if (config.ssl == true) if (config.ssl == true)
mg_set_protocol_http_websocket(nc_http); mg_set_protocol_http_websocket(nc_http);