Support for selecting and playing tracks and playlists in web interface

This commit is contained in:
Fabio Manganiello 2018-02-05 09:45:35 +01:00
parent 1cab75757b
commit 40efb3f9c7
4 changed files with 251 additions and 48 deletions

View file

@ -49,6 +49,11 @@ main {
border-radius: 0 0 7.5px 7.5px;
}
button[disabled] {
color: #bbb;
border: 1px solid;
}
.playback-controls {
text-align: center;
border-bottom: 1px solid #e8eaf0;
@ -83,22 +88,24 @@ main {
width: 78%;
}
#playlist-controls {
text-align: right;
#playlist-controls, #browser-controls {
margin-bottom: 7.5px;
padding-bottom: 7.5px;
border-bottom: 1px solid #ddd;
height: 3.8rem;
}
#playlist-content {
#playlist-controls {
text-align: right;
}
#playlist-content, #music-browser {
height: 32.2rem;
overflow-y: scroll;
}
#music-browser {
padding-top: 0;
overflow-y: scroll;
}
.music-pane {
@ -109,16 +116,21 @@ main {
.music-item {
padding: 5px;
cursor: pointer;
}
.music-item.selected {
background-color: #c8ffd0 !important;
}
.music-item:hover {
background-color: #daf8e2 !important;
}
.music-item:nth-child(odd) {
background-color: #f2f2f2;
}
.playlist-track {
cursor: pointer;
}
.playlist-track.active {
height: 4rem;
padding-top: 1.5rem;

View file

@ -1,6 +1,7 @@
$(document).ready(function() {
var seekInterval,
trackLongPressTimeout,
longPressTimeout,
curPath = [],
curTrackUpdateHandler,
curTrackElapsed = {
timestamp: null,
@ -212,16 +213,16 @@ $(document).ready(function() {
var onTrackTouchDown = function(event) {
var $track = $(this);
trackLongPressTimeout = setTimeout(function() {
longPressTimeout = setTimeout(function() {
$track.addClass('selected');
clearTimeout(trackLongPressTimeout);
trackLongPressTimeout = undefined;
clearTimeout(longPressTimeout);
longPressTimeout = undefined;
}, 1000);
};
var onTrackTouchUp = function(event) {
var $track = $(this);
if (trackLongPressTimeout) {
if (longPressTimeout) {
execute({
type: 'request',
action: 'music.mpd.playid',
@ -229,8 +230,8 @@ $(document).ready(function() {
});
}
clearTimeout(trackLongPressTimeout);
trackLongPressTimeout = undefined;
clearTimeout(longPressTimeout);
longPressTimeout = undefined;
};
var updatePlaylist = function(tracks) {
@ -288,6 +289,192 @@ $(document).ready(function() {
);
};
var onPlaylistTouchDown = function(event) {
var $playlist = $(this);
$playlist.addClass('selected');
longPressTimeout = setTimeout(function() {
clearTimeout(longPressTimeout);
longPressTimeout = undefined;
}, 1000);
};
var onPlaylistTouchUp = function(event) {
var $playlist = $(this);
if (longPressTimeout) {
execute(
{
type: 'request',
action: 'music.mpd.clear'
},
onSuccess = function() {
execute(
{
type: 'request',
action: 'music.mpd.load',
args: { playlist: $playlist.data('name') }
}
);
}
);
}
clearTimeout(longPressTimeout);
longPressTimeout = undefined;
};
var onFileTouchDown = function(event) {
var $file = $(this);
$file.addClass('selected');
longPressTimeout = setTimeout(function() {
clearTimeout(longPressTimeout);
longPressTimeout = undefined;
}, 1000);
};
var onFileTouchUp = function(event) {
var $file = $(this);
if (longPressTimeout) {
execute(
{
type: 'request',
action: 'music.mpd.playlistinfo'
},
onSuccess = function(response) {
var pos = 0;
if (response.response.output.length > 0) {
pos = parseInt(response.response.output.slice(-1)[0].pos) + 1;
}
execute(
{
type: 'request',
action: 'music.mpd.add',
args: { resource: $file.data('file') }
},
onSuccess = function() {
execute(
{
type: 'request',
action: 'music.mpd.play_pos',
args: { pos: pos }
}
);
}
)
}
);
$file.removeClass('selected');
}
clearTimeout(longPressTimeout);
longPressTimeout = undefined;
};
var onDirectorySelect = function(event) {
var $directory = $(this);
execute(
{
type: 'request',
action: 'music.mpd.lsinfo',
args: { uri: $directory.data('name') }
},
onSuccess = function(response) {
curPath = $directory.data('name').split('/')
.filter(function(term) { return term.length > 0 });
updateBrowser(response.response.output);
}
);
};
var updateBrowser = function(items) {
var $browserContent = $('#music-browser');
var $addButton = $('#browser-controls').find('button[data-action="add"]');
var directories = [];
var playlists = [];
var files = [];
$browserContent.find('.music-item').remove();
for (var item of items) {
if ('directory' in item) {
directories.push(item.directory);
} else if ('playlist' in item) {
playlists.push(item.playlist);
} else if ('file' in item) {
files.push(item);
}
}
if (curPath.length > 0) {
var $parentElement = $('<div></div>')
.addClass('browser-directory').addClass('music-item')
.addClass('browser-item').addClass('row').data('name', curPath.slice(0, -1).join('/'))
.html('<i class="fa fa-folder-open-o"></i> &nbsp; ..');
$parentElement.on('click touch', onDirectorySelect);
$parentElement.appendTo($browserContent);
$addButton.removeAttr('disabled');
} else {
$addButton.attr('disabled', 'disabled');
}
for (var directory of directories.sort()) {
var $element = $('<div></div>')
.addClass('browser-directory').addClass('music-item')
.addClass('browser-item').addClass('row').data('name', directory)
.html('<i class="fa fa-folder-open-o"></i> &nbsp; ' + directory.split('/').slice(-1)[0]);
$element.on('click touch', onDirectorySelect);
$element.appendTo($browserContent);
}
for (var playlist of playlists.sort()) {
var $element = $('<div></div>')
.addClass('browser-playlist').addClass('music-item')
.addClass('browser-item').addClass('row').data('name', playlist)
.html('<i class="fa fa-list"></i> &nbsp; ' + playlist);
$element.on('mousedown touchstart', onPlaylistTouchDown);
$element.on('mouseup touchend', onPlaylistTouchUp);
$element.appendTo($browserContent);
}
files = files.sort(function(a, b) {
if (a.artist === b.artist) {
if (a.album === b.album) {
return parseInt(a.track) < parseInt(b.track) ? -1 : 1;
} else {
return a.album.localeCompare(b.album);
}
} else {
return a.artist.localeCompare(b.artist);
}
});
for (var file of files) {
var $element = $('<div></div>')
.addClass('browser-file').addClass('music-item')
.addClass('browser-item').addClass('row')
.html('<i class="fa fa-music"></i> &nbsp; ' + file.title);
for (var prop of Object.keys(file)) {
$element.data(prop, file[prop]);
}
$element.on('mousedown touchstart', onFileTouchDown);
$element.on('mouseup touchend', onFileTouchUp);
$element.appendTo($browserContent);
}
};
var initBrowser = function() {
execute(
{
@ -296,36 +483,7 @@ $(document).ready(function() {
},
onSuccess = function(response) {
var $browserContent = $('#music-browser');
var items = response.response.output;
var directories = [];
var playlists = [];
for (var item of items) {
if ('directory' in item) {
directories.push(item.directory);
} else if ('playlist' in item) {
playlists.push(item.playlist);
}
}
for (var directory of directories.sort()) {
var $element = $('<div></div>')
.addClass('browser-directory').addClass('music-item')
.addClass('browser-item').addClass('row')
.html('<i class="fa fa-folder-open-o"></i> &nbsp; ' + directory);
$element.appendTo($browserContent);
}
for (var playlist of playlists.sort()) {
var $element = $('<div></div>')
.addClass('browser-playlist').addClass('music-item')
.addClass('browser-item').addClass('row')
.html('<i class="fa fa-music"></i> &nbsp; ' + playlist);
$element.appendTo($browserContent);
}
updateBrowser(response.response.output);
}
);
};
@ -334,6 +492,7 @@ $(document).ready(function() {
window.registerEventListener(onEvent);
var $playbackControls = $('.playback-controls, #playlist-controls').find('button');
var $playlistContent = $('#playlist-content');
var $browserAddBtn = $('#browser-controls').find('button[data-action="add"]');
var $volumeCtrl = $('#volume-ctrl');
var $trackSeeker = $('#track-seeker');
var prevVolume;
@ -408,6 +567,26 @@ $(document).ready(function() {
}
);
});
$browserAddBtn.on('click touch', function(event) {
var $browserContent = $('#music-browser');
var $items = $browserContent.find('.music-item').slice(1, -1);
var $selectedItems = $browserContent.find('.music-item.selected');
if ($selectedItems.length == 0) {
$selectedItems = $items;
}
for (var item of $selectedItems) {
execute(
{
type: 'request',
action: 'music.mpd.add',
args: { resource: $(item).data('file') }
},
);
}
});
};
var init = function() {

View file

@ -58,8 +58,16 @@
<div class="row">
<div id="player-left-side" class="three columns music-pane">
<div id="music-browser" class="music-pane"></div>
<div class="row">
<div id="browser-controls">
<button data-action="add">
<i class="fa fa-plus"></i>
</button>
</div>
<div id="music-browser" class="music-pane"></div>
</div>
</div>
<div id="player-right-side" class="nine columns music-pane">
<div class="row">
<div id="playlist-controls">

View file

@ -28,6 +28,9 @@ class MusicMpdPlugin(MusicPlugin):
self.add(resource)
return self._exec('play')
def play_pos(self, pos):
return self._exec('play', pos)
def pause(self):
status = self.status().output['state']
if status == 'play': return self._exec('pause')
@ -111,8 +114,9 @@ class MusicMpdPlugin(MusicPlugin):
return Response(output=sorted(self.client.listplaylists(),
key=lambda p: p['playlist']))
def lsinfo(self):
return Response(output=self.client.lsinfo())
def lsinfo(self, uri=None):
output = self.client.lsinfo(uri) if uri else self.client.lsinfo()
return Response(output=output)
def plchanges(self, version):
return Response(output=self.client.plchanges(version))