forked from platypush/platypush
Support for selecting and playing tracks and playlists in web interface
This commit is contained in:
parent
1cab75757b
commit
40efb3f9c7
4 changed files with 251 additions and 48 deletions
|
@ -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;
|
||||
|
|
|
@ -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> ..');
|
||||
|
||||
$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> ' + 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> ' + 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> ' + 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> ' + 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> ' + 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() {
|
||||
|
|
|
@ -58,8 +58,16 @@
|
|||
|
||||
<div class="row">
|
||||
<div id="player-left-side" class="three columns music-pane">
|
||||
<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">
|
||||
|
|
|
@ -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))
|
||||
|
|
Loading…
Reference in a new issue