Add 'Refresh' button to update libraries (#222)

Add 'Refresh' button to update libraries.

Tweak folder icons and context menus.

Fixes #136.
This commit is contained in:
John Cass 2017-01-22 07:16:52 +02:00 committed by GitHub
parent 8345fbb9c0
commit 9856685a00
7 changed files with 101 additions and 51 deletions

View File

@ -111,6 +111,7 @@ v2.4.0 (UNRELEASED)
- Add 'Show Track Info' popup which can be activated from any context menu. The popup includes the URI of the track, - Add 'Show Track Info' popup which can be activated from any context menu. The popup includes the URI of the track,
which can be inserted into various lists elsewhere in the player. which can be inserted into various lists elsewhere in the player.
- Updated icon set for font-awesome 4.7.0. - Updated icon set for font-awesome 4.7.0.
- Added 'Refresh' button for refreshing libraries. (Addresses: `#75 <https://github.com/pimusicbox/mopidy-musicbox-webclient/issues/75>`_).
**Fixes** **Fixes**
@ -121,6 +122,7 @@ v2.4.0 (UNRELEASED)
- Use correct icons for folders, audio, and other files when browsing local files. - Use correct icons for folders, audio, and other files when browsing local files.
- Now initializes the GUI properly, even if the user is offline or the Mopidy server cannot be reached. - Now initializes the GUI properly, even if the user is offline or the Mopidy server cannot be reached.
- Fixed `Alarm Clock <https://pypi.python.org/pypi/Mopidy-AlarmClock/>`_ detection. - Fixed `Alarm Clock <https://pypi.python.org/pypi/Mopidy-AlarmClock/>`_ detection.
- When browsing the library using the local 'File' extension, only playable audio files will have context menu icons.
v2.3.0 (2016-05-15) v2.3.0 (2016-05-15)
------------------- -------------------

View File

@ -118,7 +118,6 @@
/****************** /******************
* Track Slider * * Track Slider *
******************/ ******************/
#trackslider { #trackslider {
display: inline; display: inline;
width: 100%; width: 100%;
@ -366,6 +365,10 @@ span.hostInfo {
background-color: #ccc !important; background-color: #ccc !important;
} }
.refreshLibraryBtnDiv {
display: none;
}
/********************** /**********************
* Now Playing area * * Now Playing area *

View File

@ -404,7 +404,16 @@
<!--/playlistspane--> <!--/playlistspane-->
<div data-role="content" id="browsepane" class="pane"> <div data-role="content" id="browsepane" class="pane">
<div class="ui-grid-a">
<div class="ui-block-a">
<h4>Browse</h4> <h4>Browse</h4>
</div>
<div align="right" class="ui-block-b refreshLibraryBtnDiv" data-role="controlgroup" data-type="horizontal">
<button id="refreshLibraryBtn" class="btn" type="button" title="Refresh library">
<i class="fa fa-refresh"></i>
</button>
</div>
</div>
<div class="ui-grid"> <div class="ui-grid">
<ul id="browsetable" class="table"></ul> <ul id="browsetable" class="table"></ul>
</div> </div>

View File

@ -416,6 +416,14 @@
return false return false
}, },
refreshLibrary: function () {
var uri = $('#refreshLibraryBtn').data('url')
mopidy.library.refresh({'uri': uri}).then(function () {
library.getBrowseDir(uri)
})
return false
},
/** *********** /** ***********
* Buttons * * Buttons *
*************/ *************/

View File

@ -75,8 +75,8 @@ var radioExtensionsList = ['somafm', 'tunein', 'dirble', 'audioaddict']
var uriClassList = [ var uriClassList = [
['spotify', 'fa-spotify'], ['spotify', 'fa-spotify'],
['spotifytunigo', 'fa-spotify'], ['spotifytunigo', 'fa-spotify'],
['local', 'fa-file-sound-o'], ['local', 'fa-folder-o'],
['file', 'fa-file-o'], ['file', 'fa-folder-o'],
['m3u', 'fa-file-sound-o'], ['m3u', 'fa-file-sound-o'],
['podcast', 'fa-rss-square'], ['podcast', 'fa-rss-square'],
['podcast+file', 'fa-rss-square'], ['podcast+file', 'fa-rss-square'],
@ -240,13 +240,9 @@ function renderSongLi (previousTrack, track, nextTrack, uri, tlid, target, curre
var onClick = '' var onClick = ''
var html = '' var html = ''
track.name = validateTrackName(track, currentIndex) track.name = validateTrackName(track, currentIndex)
// Leave out unplayable items
if (track.name.substring(0, 12) === '[unplayable]') {
return html
}
// Streams // Streams
if (track.length === -1) { if (track.length === -1) {
html += '<li class="albumli"><a href="#"><h1><i class="' + getMediaClass(track.uri) + '"></i> ' + track.name + ' [Stream]</h1></a></li>' html += '<li class="albumli"><a href="#"><h1><i class="' + getMediaClass(track) + '"></i> ' + track.name + ' [Stream]</h1></a></li>'
return html return html
} }
@ -257,11 +253,13 @@ function renderSongLi (previousTrack, track, nextTrack, uri, tlid, target, curre
onClick = 'return controls.playTracks(\'\', mopidy, \'' + track.uri + '\', \'' + uri + '\');' onClick = 'return controls.playTracks(\'\', mopidy, \'' + track.uri + '\', \'' + uri + '\');'
} }
html += html += '<li class="song albumli" id="' + getjQueryID(target, track.uri) + '" tlid="' + tlid + '">'
'<li class="song albumli" id="' + getjQueryID(target, track.uri) + '" tlid="' + tlid + '">' + if (isPlayable(track)) {
'<a href="#" class="moreBtn" onclick="return popupTracks(event, \'' + uri + '\',\'' + track.uri + tlidParameter + '\');">' + // Show popup icon for audio files or 'tracks' of other scheme types
'<i class="fa fa-play-circle-o"></i></a>' + html += '<a href="#" class="moreBtn" onclick="return popupTracks(event, \'' + uri + '\',\'' + track.uri + tlidParameter + '\');">' +
'<a href="#" onclick="' + onClick + '"><h1><i class="' + getMediaClass(track.uri) + '"></i> ' + track.name + '</h1>' '<i class="fa fa-play-circle-o"></i></a>'
}
html += '<a href="#" onclick="' + onClick + '"><h1><i class="' + getMediaClass(track) + '"></i> ' + track.name + '</h1>'
if (listLength === 1 || (!hasSameAlbum(previousTrack, track) && !hasSameAlbum(track, nextTrack))) { if (listLength === 1 || (!hasSameAlbum(previousTrack, track) && !hasSameAlbum(track, nextTrack))) {
html += renderSongLiAlbumInfo(track) html += renderSongLiAlbumInfo(track)
@ -309,7 +307,7 @@ function renderSongLiDivider (previousTrack, track, nextTrack, target) {
html += html +=
'<li class="albumdivider"><a href="#" onclick="return library.showAlbum(\'' + track.album.uri + '\');">' + '<li class="albumdivider"><a href="#" onclick="return library.showAlbum(\'' + track.album.uri + '\');">' +
'<img id="' + getjQueryID(target + '-cover', track.uri) + '" class="artistcover" width="30" height="30"/>' + '<img id="' + getjQueryID(target + '-cover', track.uri) + '" class="artistcover" width="30" height="30"/>' +
'<h1><i class="' + getMediaClass(track.uri) + '"></i> ' + track.album.name + '</h1><p>' + '<h1>' + track.album.name + '</h1><p>' +
renderSongLiTrackArtists(track) + '</p></a></li>' renderSongLiTrackArtists(track) + '</p></a></li>'
// Retrieve album covers // Retrieve album covers
images.setAlbumImage(track.uri, getjQueryID(target + '-cover', track.uri, true), mopidy, 'small') images.setAlbumImage(track.uri, getjQueryID(target + '-cover', track.uri, true), mopidy, 'small')
@ -317,7 +315,7 @@ function renderSongLiDivider (previousTrack, track, nextTrack, target) {
// Small divider // Small divider
html += '<li class="smalldivider"> &nbsp;</li>' html += '<li class="smalldivider"> &nbsp;</li>'
} }
if (typeof target !== 'undefined' && target.length > 0) { if (html.length > 0 && typeof target !== 'undefined' && target.length > 0) {
target = getjQueryID(target, track.uri, true) target = getjQueryID(target, track.uri, true)
$(target).before(html) $(target).before(html)
} }
@ -494,27 +492,53 @@ function getScheme (uri) {
return uri.split(':')[0].toLowerCase() return uri.split(':')[0].toLowerCase()
} }
function isAudioFile (uri) { function isPlayable (track) {
var ext = uri.split('.').pop().toLowerCase() if (typeof track.type === 'undefined' || track.type === 'track') {
return $.inArray(ext, audioExt) !== -1 if (getScheme(track.uri) === 'file') {
var ext = track.uri.split('.').pop().toLowerCase()
if ($.inArray(ext, audioExt) === -1) {
// Files must have the correct extension
return false
}
}
return true
}
return false
} }
function isStreamUri (uri) { function isStreamUri (uri) {
var a = validUri(uri) return validUri(uri) || radioExtensionsList.indexOf(getScheme(uri)) >= 0
var b = radioExtensionsList.indexOf(getScheme(uri)) >= 0
return a || b
} }
function getMediaClass (uri) { function getMediaClass (track) {
var scheme = getScheme(uri) var type = track.type
if (scheme === 'file' && isAudioFile(uri)) { if (typeof type === 'undefined' || type === 'track') {
return 'fa fa-file-sound-o' if (isPlayable(track)) {
if (isStreamUri(track.uri)) {
return 'fa fa-rss' // Stream
} else {
return 'fa fa-file-sound-o' // Sound file (default)
} }
} else {
return 'fa fa-file-o' // Unplayable file
}
} else if (type === 'directory') {
for (var i = 0; i < uriClassList.length; i++) { for (var i = 0; i < uriClassList.length; i++) {
if (scheme === uriClassList[i][0]) { if (getScheme(track.uri) === uriClassList[i][0]) {
return 'fa ' + uriClassList[i][1] return 'fa ' + uriClassList[i][1] // Mapped service directory
} }
} }
return 'fa fa-folder-o' // Unmapped directory
} else if (type === 'album') {
// return 'fa fa-bullseye' // Album
return 'fa fa-folder-o'
} else if (type === 'artist') {
// return 'fa fa-user-circle-o' // Artist
return 'fa fa-folder-o'
} else if (type === 'playlist') {
// return 'fa fa-star' // Playlist
return ''
}
return '' return ''
} }

View File

@ -145,7 +145,7 @@
tokens = { tokens = {
'id': results.artists[i].uri, 'id': results.artists[i].uri,
'name': results.artists[i].name, 'name': results.artists[i].name,
'class': getMediaClass(results.artists[i].uri) 'class': getMediaClass(results.artists[i])
} }
// Add 'Show all' item after a certain number of hits. // Add 'Show all' item after a certain number of hits.
@ -173,7 +173,7 @@
'albumName': results.albums[i].name, 'albumName': results.albums[i].name,
'artistName': '', 'artistName': '',
'albumYear': results.albums[i].date, 'albumYear': results.albums[i].date,
'class': getMediaClass(results.albums[i].uri) 'class': getMediaClass(results.albums[i])
} }
if (results.albums[i].artists) { if (results.albums[i].artists) {
for (j = 0; j < results.albums[i].artists.length; j++) { for (j = 0; j < results.albums[i].artists.length; j++) {
@ -215,14 +215,23 @@
showLoading(true) showLoading(true)
if (!rootdir) { if (!rootdir) {
browseStack.pop() browseStack.pop()
rootdir = browseStack[browseStack.length - 1] rootdir = browseStack[browseStack.length - 1] || null
} else { } else {
if (rootdir !== browseStack[browseStack.length - 1]) {
browseStack.push(rootdir) browseStack.push(rootdir)
} }
if (!rootdir) {
rootdir = null
} }
mopidy.library.browse({'uri': rootdir}).then(processBrowseDir, console.error) mopidy.library.browse({'uri': rootdir}).then(function (resultArr) {
processBrowseDir(resultArr)
if (rootdir === null) {
$('.refreshLibraryBtnDiv').hide()
} else {
$('.refreshLibraryBtnDiv').show()
$('#refreshLibraryBtn').data('url', rootdir)
$('#refreshLibraryBtn').off('click')
$('#refreshLibraryBtn').one('click', controls.refreshLibrary)
}
}, console.error)
}, },
getCurrentPlaylist: function () { getCurrentPlaylist: function () {

View File

@ -90,8 +90,9 @@ function processBrowseDir (resultArr) {
var length = 0 || resultArr.length var length = 0 || resultArr.length
customTracklists[BROWSE_TABLE] = [] customTracklists[BROWSE_TABLE] = []
var html = '' var html = ''
var i
for (var i = 0, index = 0; i < resultArr.length; i++) { for (i = 0, index = 0; i < resultArr.length; i++) {
if (resultArr[i].type === 'track') { if (resultArr[i].type === 'track') {
previousRef = ref || undefined previousRef = ref || undefined
nextRef = i < resultArr.length - 1 ? resultArr[i + 1] : undefined nextRef = i < resultArr.length - 1 ? resultArr[i + 1] : undefined
@ -105,14 +106,8 @@ function processBrowseDir (resultArr) {
index++ index++
} else { } else {
var iconClass = ''
if (browseStack.length > 0 && resultArr[i].type === 'directory') {
iconClass = 'fa fa-folder-o'
} else {
iconClass = getMediaClass(resultArr[i].uri)
}
html += '<li><a href="#" onclick="return library.getBrowseDir(this.id);" id="' + resultArr[i].uri + '">' + html += '<li><a href="#" onclick="return library.getBrowseDir(this.id);" id="' + resultArr[i].uri + '">' +
'<h1><i class="' + iconClass + '"></i> ' + resultArr[i].name + '</h1></a></li>' '<h1><i class="' + getMediaClass(resultArr[i]) + '"></i> ' + resultArr[i].name + '</h1></a></li>'
} }
} }
@ -124,22 +119,22 @@ function processBrowseDir (resultArr) {
mopidy.library.lookup({'uris': uris}).then(function (resultDict) { mopidy.library.lookup({'uris': uris}).then(function (resultDict) {
// Break into albums and put in tables // Break into albums and put in tables
var track, previousTrack, nextTrack, uri var track, previousTrack, nextTrack, uri
$.each(resultArr, function (i, ref) { for (i = 0, index = 0; i < resultArr.length; i++) {
if (ref.type === 'track') { if (resultArr[i].type === 'track') {
previousTrack = track || undefined previousTrack = track || undefined
if (i < resultArr.length - 1 && resultDict[resultArr[i + 1].uri]) { if (i < resultArr.length - 1 && resultDict[resultArr[i + 1].uri]) {
nextTrack = resultDict[resultArr[i + 1].uri][0] nextTrack = resultDict[resultArr[i + 1].uri][0]
} else { } else {
nextTrack = undefined nextTrack = undefined
} }
track = resultDict[ref.uri][0] track = resultDict[resultArr[i].uri][0]
popupData[track.uri] = track // Need full track info in popups in order to display albums and artists. popupData[track.uri] = track // Need full track info in popups in order to display albums and artists.
if (uris.length === 1 || (previousTrack && !hasSameAlbum(previousTrack, track) && !hasSameAlbum(track, nextTrack))) { if (uris.length === 1 || (previousTrack && !hasSameAlbum(previousTrack, track) && !hasSameAlbum(track, nextTrack))) {
renderSongLiAlbumInfo(track, BROWSE_TABLE) renderSongLiAlbumInfo(track, BROWSE_TABLE)
} }
renderSongLiDivider(previousTrack, track, nextTrack, BROWSE_TABLE) renderSongLiDivider(previousTrack, track, nextTrack, BROWSE_TABLE)
} }
}) }
showLoading(false) showLoading(false)
}, console.error) }, console.error)
} else { } else {
@ -166,7 +161,7 @@ function processGetPlaylists (resultArr) {
} else if (isFavouritesPlaylist(resultArr[i])) { } else if (isFavouritesPlaylist(resultArr[i])) {
favourites = li_html + '&hearts; Musicbox Favourites</a></li>' favourites = li_html + '&hearts; Musicbox Favourites</a></li>'
} else { } else {
tmp = tmp + li_html + '<i class="' + getMediaClass(resultArr[i].uri) + '"></i> ' + resultArr[i].name + '</a></li>' tmp = tmp + li_html + '<i class="' + getMediaClass(resultArr[i]) + '"></i> ' + resultArr[i].name + '</a></li>'
} }
} }
// Prepend the user's Spotify "Starred" playlist and favourites to the results. (like Spotify official client). // Prepend the user's Spotify "Starred" playlist and favourites to the results. (like Spotify official client).