mirror of
https://github.com/SuperBFG7/ympd
synced 2025-01-28 01:44:55 +00:00
feat: drag & drop reorder songs in playlist
This commit is contained in:
parent
7fc18e4a65
commit
772b6471cc
143
htdocs/js/mpd.js
143
htdocs/js/mpd.js
@ -367,67 +367,6 @@ function appInit() {
|
|||||||
toggleBtn(event.target.id);
|
toggleBtn(event.target.id);
|
||||||
}, false);
|
}, false);
|
||||||
|
|
||||||
var queueBody=document.getElementById('QueueList').getElementsByTagName('tbody')[0];
|
|
||||||
queueBody.addEventListener('dragstart', function(event) {
|
|
||||||
if (event.target.nodeName == 'TR') {
|
|
||||||
event.target.classList.add('opacity05');
|
|
||||||
event.dataTransfer.setDragImage(event.target, 0, 0);
|
|
||||||
event.dataTransfer.effectAllowed = 'move';
|
|
||||||
event.dataTransfer.setData('Text', event.target.getAttribute('id'));
|
|
||||||
dragEl = event.target.cloneNode(true);
|
|
||||||
}
|
|
||||||
}, false);
|
|
||||||
queueBody.addEventListener('dragleave', function(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
var target = event.target;
|
|
||||||
if (event.target.nodeName == 'TD')
|
|
||||||
target = event.target.parentNode;
|
|
||||||
if (target.nodeName == 'TR')
|
|
||||||
target.classList.remove('dragover');
|
|
||||||
}, false);
|
|
||||||
queueBody.addEventListener('dragover', function(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
var tr = queueBody.querySelectorAll('.dragover');
|
|
||||||
var trLen = tr.length;
|
|
||||||
for (var i = 0; i < trLen; i++) {
|
|
||||||
tr[i].classList.remove('dragover');
|
|
||||||
}
|
|
||||||
var target = event.target;
|
|
||||||
if (event.target.nodeName == 'TD')
|
|
||||||
target = event.target.parentNode;
|
|
||||||
if (target.nodeName == 'TR')
|
|
||||||
target.classList.add('dragover');
|
|
||||||
event.dataTransfer.dropEffect = 'move';
|
|
||||||
}, false);
|
|
||||||
queueBody.addEventListener('dragend', function(event) {
|
|
||||||
var tr = queueBody.querySelectorAll('.dragover');
|
|
||||||
var trLen = tr.length;
|
|
||||||
for (var i = 0; i < trLen; i++) {
|
|
||||||
tr[i].classList.remove('dragover');
|
|
||||||
}
|
|
||||||
if (document.getElementById(event.dataTransfer.getData('Text')))
|
|
||||||
document.getElementById(event.dataTransfer.getData('Text')).classList.remove('opacity05');
|
|
||||||
}, false);
|
|
||||||
queueBody.addEventListener('drop', function(event) {
|
|
||||||
event.stopPropagation();
|
|
||||||
event.preventDefault();
|
|
||||||
var target = event.target;
|
|
||||||
if (event.target.nodeName == 'TD')
|
|
||||||
target = event.target.parentNode;
|
|
||||||
var oldSongpos = document.getElementById(event.dataTransfer.getData('Text')).getAttribute('data-songpos');
|
|
||||||
var newSongpos = target.getAttribute('data-songpos');
|
|
||||||
document.getElementById(event.dataTransfer.getData('Text')).remove();
|
|
||||||
dragEl.classList.remove('opacity05');
|
|
||||||
queueBody.insertBefore(dragEl, target);
|
|
||||||
var tr = queueBody.querySelectorAll('.dragover');
|
|
||||||
var trLen = tr.length;
|
|
||||||
for (var i = 0; i < trLen; i++) {
|
|
||||||
tr[i].classList.remove('dragover');
|
|
||||||
}
|
|
||||||
document.getElementById('QueueList').classList.add('opacity05');
|
|
||||||
sendAPI({"cmd": "MPD_API_MOVE_TRACK","data": {"from": oldSongpos, "to": newSongpos}});
|
|
||||||
}, false);
|
|
||||||
|
|
||||||
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_PLAY_TRACK","data": {"track": event.target.parentNode.getAttribute('data-trackid')}});
|
sendAPI({"cmd": "MPD_API_PLAY_TRACK","data": {"track": event.target.parentNode.getAttribute('data-trackid')}});
|
||||||
@ -519,6 +458,9 @@ function appInit() {
|
|||||||
appGoto('Search', undefined, undefined, '0/' + app.current.filter + '/' + this.value);
|
appGoto('Search', undefined, undefined, '0/' + app.current.filter + '/' + this.value);
|
||||||
}, false);
|
}, false);
|
||||||
|
|
||||||
|
dragAndDropTable('QueueList');
|
||||||
|
dragAndDropTable('BrowsePlaylistsDetailList');
|
||||||
|
|
||||||
window.addEventListener('hashchange', appRoute, false);
|
window.addEventListener('hashchange', appRoute, false);
|
||||||
|
|
||||||
document.addEventListener('keydown', function(event) {
|
document.addEventListener('keydown', function(event) {
|
||||||
@ -587,6 +529,77 @@ function appInit() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function dragAndDropTable(table) {
|
||||||
|
var tableBody=document.getElementById(table).getElementsByTagName('tbody')[0];
|
||||||
|
tableBody.addEventListener('dragstart', function(event) {
|
||||||
|
if (event.target.nodeName == 'TR') {
|
||||||
|
event.target.classList.add('opacity05');
|
||||||
|
event.dataTransfer.setDragImage(event.target, 0, 0);
|
||||||
|
event.dataTransfer.effectAllowed = 'move';
|
||||||
|
event.dataTransfer.setData('Text', event.target.getAttribute('id'));
|
||||||
|
dragEl = event.target.cloneNode(true);
|
||||||
|
}
|
||||||
|
}, false);
|
||||||
|
tableBody.addEventListener('dragleave', function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
var target = event.target;
|
||||||
|
if (event.target.nodeName == 'TD')
|
||||||
|
target = event.target.parentNode;
|
||||||
|
if (target.nodeName == 'TR')
|
||||||
|
target.classList.remove('dragover');
|
||||||
|
}, false);
|
||||||
|
tableBody.addEventListener('dragover', function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
var tr = tableBody.querySelectorAll('.dragover');
|
||||||
|
var trLen = tr.length;
|
||||||
|
for (var i = 0; i < trLen; i++) {
|
||||||
|
tr[i].classList.remove('dragover');
|
||||||
|
}
|
||||||
|
var target = event.target;
|
||||||
|
if (event.target.nodeName == 'TD')
|
||||||
|
target = event.target.parentNode;
|
||||||
|
if (target.nodeName == 'TR')
|
||||||
|
target.classList.add('dragover');
|
||||||
|
event.dataTransfer.dropEffect = 'move';
|
||||||
|
}, false);
|
||||||
|
tableBody.addEventListener('dragend', function(event) {
|
||||||
|
var tr = tableBody.querySelectorAll('.dragover');
|
||||||
|
var trLen = tr.length;
|
||||||
|
for (var i = 0; i < trLen; i++) {
|
||||||
|
tr[i].classList.remove('dragover');
|
||||||
|
}
|
||||||
|
if (document.getElementById(event.dataTransfer.getData('Text')))
|
||||||
|
document.getElementById(event.dataTransfer.getData('Text')).classList.remove('opacity05');
|
||||||
|
}, false);
|
||||||
|
tableBody.addEventListener('drop', function(event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
var target = event.target;
|
||||||
|
if (event.target.nodeName == 'TD')
|
||||||
|
target = event.target.parentNode;
|
||||||
|
var oldSongpos = document.getElementById(event.dataTransfer.getData('Text')).getAttribute('data-songpos');
|
||||||
|
var newSongpos = target.getAttribute('data-songpos');
|
||||||
|
document.getElementById(event.dataTransfer.getData('Text')).remove();
|
||||||
|
dragEl.classList.remove('opacity05');
|
||||||
|
tableBody.insertBefore(dragEl, target);
|
||||||
|
var tr = tableBody.querySelectorAll('.dragover');
|
||||||
|
var trLen = tr.length;
|
||||||
|
for (var i = 0; i < trLen; i++) {
|
||||||
|
tr[i].classList.remove('dragover');
|
||||||
|
}
|
||||||
|
document.getElementById(table).classList.add('opacity05');
|
||||||
|
if (app.current.app == 'Queue')
|
||||||
|
sendAPI({"cmd": "MPD_API_MOVE_TRACK","data": {"from": oldSongpos, "to": newSongpos}});
|
||||||
|
else if (app.current.app == 'Browse' && app.current.tab == 'Playlists' && app.current.view == 'Detail')
|
||||||
|
playlistMoveTrack(oldSongpos, newSongpos);
|
||||||
|
}, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function playlistMoveTrack(from, to) {
|
||||||
|
sendAPI({"cmd": "MPD_API_PLAYLIST_MOVE_TRACK","data": { "plist": app.current.search, "from": from, "to": to}});
|
||||||
|
sendAPI({"cmd": "MPD_API_GET_PLAYLIST_LIST","data": {"offset": app.current.page, "filter": app.current.filter, "uri": app.current.search}}, parsePlaylists);
|
||||||
|
}
|
||||||
|
|
||||||
function webSocketConnect() {
|
function webSocketConnect() {
|
||||||
socket = new WebSocket(getWsUrl());
|
socket = new WebSocket(getWsUrl());
|
||||||
|
|
||||||
@ -1047,18 +1060,20 @@ function parsePlaylists(obj) {
|
|||||||
else if (app.current.view == 'Detail') {
|
else if (app.current.view == 'Detail') {
|
||||||
for (var i = 0; i < nrItems; i++) {
|
for (var i = 0; i < nrItems; i++) {
|
||||||
var uri = encodeURI(obj.data[i].uri);
|
var uri = encodeURI(obj.data[i].uri);
|
||||||
|
var songpos = obj.offset + i + 1;
|
||||||
if (tr[i])
|
if (tr[i])
|
||||||
if (tr[i].getAttribute('data-uri') == uri)
|
if (tr[i].getAttribute('data-uri') == uri && tr[i].getAttribute('id') == 'playlistTrackId' + songpos)
|
||||||
continue;
|
continue;
|
||||||
var songpos = obj.offset + i;
|
|
||||||
var row = document.createElement('tr');
|
var row = document.createElement('tr');
|
||||||
|
row.setAttribute('draggable','true');
|
||||||
|
row.setAttribute('id','playlistTrackId' + songpos);
|
||||||
row.setAttribute('data-type', obj.data[i].type);
|
row.setAttribute('data-type', obj.data[i].type);
|
||||||
row.setAttribute('data-uri', uri);
|
row.setAttribute('data-uri', uri);
|
||||||
row.setAttribute('data-name', obj.data[i].name);
|
row.setAttribute('data-name', obj.data[i].name);
|
||||||
row.setAttribute('data-songpos', songpos);
|
row.setAttribute('data-songpos', songpos);
|
||||||
var minutes = Math.floor(obj.data[i].duration / 60);
|
var minutes = Math.floor(obj.data[i].duration / 60);
|
||||||
var seconds = obj.data[i].duration - minutes * 60;
|
var seconds = obj.data[i].duration - minutes * 60;
|
||||||
row.innerHTML = '<td>' + (songpos + 1) + '</td>' +
|
row.innerHTML = '<td>' + songpos + '</td>' +
|
||||||
'<td>' + obj.data[i].title + '</td>' +
|
'<td>' + obj.data[i].title + '</td>' +
|
||||||
'<td>' + obj.data[i].artist + '</td>' +
|
'<td>' + obj.data[i].artist + '</td>' +
|
||||||
'<td>' + obj.data[i].album + '</td>' +
|
'<td>' + obj.data[i].album + '</td>' +
|
||||||
|
@ -183,12 +183,27 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg)
|
|||||||
case MPD_API_MOVE_TRACK:
|
case MPD_API_MOVE_TRACK:
|
||||||
je = json_scanf(msg.p, msg.len, "{ data: { from:%u, to:%u } }", &uint_buf1, &uint_buf2);
|
je = json_scanf(msg.p, msg.len, "{ data: { from:%u, to:%u } }", &uint_buf1, &uint_buf2);
|
||||||
if (je == 2) {
|
if (je == 2) {
|
||||||
uint_buf1 -= 1;
|
uint_buf1 --;
|
||||||
uint_buf2 -= 1;
|
uint_buf2 --;
|
||||||
|
if (uint_buf1 < uint_buf2)
|
||||||
|
uint_buf2 --;
|
||||||
mpd_run_move(mpd.conn, uint_buf1, uint_buf2);
|
mpd_run_move(mpd.conn, uint_buf1, uint_buf2);
|
||||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}");
|
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case MPD_API_PLAYLIST_MOVE_TRACK:
|
||||||
|
je = json_scanf(msg.p, msg.len, "{ data: { plist: %Q, from:%u, to:%u } }", &p_charbuf1, &uint_buf1, &uint_buf2);
|
||||||
|
if (je == 3) {
|
||||||
|
uint_buf1 --;
|
||||||
|
uint_buf2 --;
|
||||||
|
if (uint_buf1 < uint_buf2)
|
||||||
|
uint_buf2 --;
|
||||||
|
mpd_send_playlist_move(mpd.conn, p_charbuf1, uint_buf1, uint_buf2);
|
||||||
|
mpd_response_finish(mpd.conn);
|
||||||
|
free(p_charbuf1);
|
||||||
|
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}");
|
||||||
|
}
|
||||||
|
break;
|
||||||
case MPD_API_PLAY_TRACK:
|
case MPD_API_PLAY_TRACK:
|
||||||
je = json_scanf(msg.p, msg.len, "{ data: { track:%u } }", &uint_buf1);
|
je = json_scanf(msg.p, msg.len, "{ data: { track:%u } }", &uint_buf1);
|
||||||
if (je == 1) {
|
if (je == 1) {
|
||||||
@ -1170,11 +1185,11 @@ int mympd_put_playlist_list(char *buffer, char *uri, unsigned int offset, char *
|
|||||||
if (entities_returned ++) len += json_printf(&out,",");
|
if (entities_returned ++) len += json_printf(&out,",");
|
||||||
len += json_printf(&out, "{type: song, uri: %Q, album: %Q, artist: %Q, duration: %d, title: %Q, name: %Q }",
|
len += json_printf(&out, "{type: song, uri: %Q, album: %Q, artist: %Q, duration: %d, title: %Q, name: %Q }",
|
||||||
mpd_song_get_uri(song),
|
mpd_song_get_uri(song),
|
||||||
mympd_get_tag(song, MPD_TAG_ALBUM),
|
mympd_get_tag(song, MPD_TAG_ALBUM),
|
||||||
mympd_get_tag(song, MPD_TAG_ARTIST),
|
mympd_get_tag(song, MPD_TAG_ARTIST),
|
||||||
mpd_song_get_duration(song),
|
mpd_song_get_duration(song),
|
||||||
entityName,
|
entityName,
|
||||||
entityName
|
entityName
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
entity_count --;
|
entity_count --;
|
||||||
|
@ -55,6 +55,7 @@
|
|||||||
X(MPD_API_RM_PLAYLIST_TRACK) \
|
X(MPD_API_RM_PLAYLIST_TRACK) \
|
||||||
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_ADD_TO_PLAYLIST) \
|
X(MPD_API_ADD_TO_PLAYLIST) \
|
||||||
X(MPD_API_PLAY_TRACK) \
|
X(MPD_API_PLAY_TRACK) \
|
||||||
X(MPD_API_SAVE_QUEUE) \
|
X(MPD_API_SAVE_QUEUE) \
|
||||||
|
Loading…
Reference in New Issue
Block a user