diff --git a/README.rst b/README.rst index 9cdb830..9c2c186 100644 --- a/README.rst +++ b/README.rst @@ -108,8 +108,8 @@ v2.4.0 (UNRELEASED) - Now shows server name/IP address and port number at the bottom of the navigation pane. (Addresses: `#67 `_). - Add ability to insert a track anywhere in the current queue. (Addresses: `#75 `_). -- 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. +- Add 'Show Track Info' popup which can be activated from any context menu or by clicking the 'info' icon next to the album cover on the + 'Now Playing' pane. The popup includes the URI of the track, which can be inserted into various lists elsewhere in the player. - Updated icon set for font-awesome 4.7.0. - Added 'Refresh' button for refreshing libraries. (Addresses: `#75 `_). @@ -123,6 +123,7 @@ v2.4.0 (UNRELEASED) - Now initializes the GUI properly, even if the user is offline or the Mopidy server cannot be reached. - Fixed `Alarm Clock `_ detection. - When browsing the library using the local 'File' extension, only playable audio files will have context menu icons. +- Show all available track information in the 'Show Track Info...' popup. (Fixes: `#227 `_). v2.3.0 (2016-05-15) ------------------- diff --git a/mopidy_musicbox_webclient/static/css/webclient.css b/mopidy_musicbox_webclient/static/css/webclient.css index 6d4efc8..63afce0 100644 --- a/mopidy_musicbox_webclient/static/css/webclient.css +++ b/mopidy_musicbox_webclient/static/css/webclient.css @@ -291,6 +291,12 @@ span.hostInfo { vertical-align: middle; } +.info-table input { + color: #555; + border: none; + font-size: 1em; +} + .albumdivider h1, .table li h1 { font-size: 120% !important; } @@ -361,6 +367,21 @@ span.hostInfo { font-size: initial; } +.infoBtn { + top: 0; + width: 90%; + position: absolute; +} + +.infoBtn i { + font-size: 1.33em; + color: #ddd; + background: white; + border-radius: 50%; + height: 1em; + width: 1em; +} + .backnav { background-color: #ccc !important; } @@ -401,11 +422,17 @@ span.hostInfo { /************ * Popups * ************/ -#modalalbum a, #modalartist a { +#modalalbum a, #modalartist a, #modalname a { color: #444; text-decoration: none; } +#modalinfo { + position: relative; + display: inline-block; + padding-top: .5em; +} + .popupArtistLi, .popupAlbumLi { display: none diff --git a/mopidy_musicbox_webclient/static/index.html b/mopidy_musicbox_webclient/static/index.html index 38aedd7..2928022 100644 --- a/mopidy_musicbox_webclient/static/index.html +++ b/mopidy_musicbox_webclient/static/index.html @@ -130,13 +130,13 @@
  • Play All
  • -
  • +
  • Play Track Next
  • -
  • +
  • Add Track to Bottom of Queue
  • -
  • +
  • Add All to Bottom of Queue
  • @@ -151,7 +151,7 @@
    • - Show Track Info... + Show Track Info...
    • @@ -181,7 +181,7 @@
      • - Show Track Info... + Show Track Info...
      • @@ -260,36 +260,7 @@ - - - Name: - - - - Album: - - - - Artist(s): - - - - Track #: - - - - Length: - - - - Bitrate: - - - - URI: - - - + @@ -363,10 +334,12 @@
        - Album cover +
        + Album cover +
        -

        +

        -

        diff --git a/mopidy_musicbox_webclient/static/js/controls.js b/mopidy_musicbox_webclient/static/js/controls.js index 75bb2a6..4fe13fe 100644 --- a/mopidy_musicbox_webclient/static/js/controls.js +++ b/mopidy_musicbox_webclient/static/js/controls.js @@ -338,74 +338,130 @@ }) }, - showInfoPopup: function (popupId, mopidy) { + showInfoPopup: function (uri, popupId, mopidy) { showLoading(true) - var uri = $(popupId).data('track') + var trackUri = uri || $(popupId).data('track') $(popupId).popup('close') - mopidy.library.lookup({'uris': [uri]}).then(function (resultDict) { + $('#popupShowInfo tbody').empty() + + mopidy.library.lookup({'uris': [trackUri]}).then(function (resultDict) { var uri = Object.keys(resultDict)[0] var track = resultDict[uri][0] + var html = '' + var rowTemplate = '{label}:{text}' + var row = {'label': '', 'text': ''} + + row.label = 'Name' if (track.name) { - $('#popupShowInfo #name-cell').text(track.name) + row.text = track.name } else { - $('#popupShowInfo #name-cell').text('(Not available)') + row.text = '(Not available)' } + html += stringFromTemplate(rowTemplate, row) + row.label = 'Album' if (track.album && track.album.name) { - $('#popupShowInfo #album-cell').text(track.album.name) + row.text = track.album.name } else { - $('#popupShowInfo #album-cell').text('(Not available)') + row.text = '(Not available)' } - var artistNames = '' - if (track.artists && track.artists.length > 0) { - for (var i = 0; i < track.artists.length; i++) { - if (i > 0) { - artistNames = artistNames + ', ' - } - artistNames = artistNames + track.artists[i].name - } + html += stringFromTemplate(rowTemplate, row) + + var artists = artistsToString(track.artists) + // Fallback to album artists. + if (artists.length === 0 && track.album && track.album.artists) { + artists = artistsToString(track.album.artists) } - // Fallback to album artists. - if (artistNames.length === 0 && track.album && track.album.artists && track.album.artists.length > 0) { - for (i = 0; i < track.album.artists.length; i++) { - if (i > 0) { - artistNames = artistNames + ', ' - } - artistNames = artistNames + track.album.artists[i].name + if (artists.length > 0) { + if (track.artists && track.artists.length > 1 || track.album && track.album.artists && track.album.artists.length > 1) { + row.label = 'Artists' + } else { + row.label = 'Artist' } + row.text = artists + html += stringFromTemplate(rowTemplate, row) } - if (artistNames.length > 0) { - $('#popupShowInfo #artist-cell').text(artistNames) - $('#popupShowInfo #artist-row').show() - } else { - $('#popupShowInfo #artist-row').hide() + + var composers = artistsToString(track.composers) + if (composers.length > 0) { + if (track.composers.length > 1) { + row.label = 'Composers' + } else { + row.label = 'Composer' + } + row.text = composers + html += stringFromTemplate(rowTemplate, row) } + + var performers = artistsToString(track.performers) + if (performers.length > 0) { + if (track.performers.length > 1) { + row.label = 'Performers' + } else { + row.label = 'Performer' + } + row.text = performers + html += stringFromTemplate(rowTemplate, row) + } + + if (track.genre) { + row = {'label': 'Genre', 'text': track.genre} + html += stringFromTemplate(rowTemplate, row) + } + if (track.track_no) { - $('#popupShowInfo #track-no-cell').text(track.track_no) - $('#popupShowInfo #track-no-row').show() - } else { - $('#popupShowInfo #track-no-row').hide() + row = {'label': 'Track #', 'text': track.track_no} + html += stringFromTemplate(rowTemplate, row) } + + if (track.disc_no) { + row = {'label': 'Disc #', 'text': track.disc_no} + html += stringFromTemplate(rowTemplate, row) + } + + if (track.date) { + row = {'label': 'Date', 'text': new Date(track.date)} + html += stringFromTemplate(rowTemplate, row) + } + if (track.length) { - $('#popupShowInfo #length-cell').text(timeFromSeconds(track.length / 1000)) - $('#popupShowInfo #length-row').show() - } else { - $('#popupShowInfo #length-row').hide() + row = {'label': 'Length', 'text': timeFromSeconds(track.length / 1000)} + html += stringFromTemplate(rowTemplate, row) } + if (track.bitrate) { - $('#popupShowInfo #bitrate-cell').text(track.bitrate) - $('#popupShowInfo #bitrate-row').show() - } else { - $('#popupShowInfo #bitrate-row').hide() + row = {'label': 'Bitrate', 'text': track.bitrate} + html += stringFromTemplate(rowTemplate, row) } - $('#popupShowInfo #uri-cell').val(uri) + + if (track.comment) { + row = {'label': 'Comment', 'text': track.comment} + html += stringFromTemplate(rowTemplate, row) + } + + if (track.musicbrainz_id) { + row = {'label': 'MusicBrainz ID', 'text': track.musicbrainz_id} + html += stringFromTemplate(rowTemplate, row) + } + + if (track.last_modified) { + row = {'label': 'Modified', 'text': track.last_modified} + html += stringFromTemplate(rowTemplate, row) + } + + rowTemplate = '{label}:' + row = {'label': 'URI', 'text': uri} + html += stringFromTemplate(rowTemplate, row) + + $('#popupShowInfo tbody').append(html) + showLoading(false) $('#popupShowInfo').popup('open') if (!isMobile) { // Set focus and select URI text on desktop systems (don't want the keyboard to pop up automatically on mobile devices) - $('#popupShowInfo #uri-cell').focus() - $('#popupShowInfo #uri-cell').select() + $('#popupShowInfo #uri-input').focus() + $('#popupShowInfo #uri-input').select() } }, console.error) return false diff --git a/mopidy_musicbox_webclient/static/js/functionsvars.js b/mopidy_musicbox_webclient/static/js/functionsvars.js index 9752563..54aef27 100644 --- a/mopidy_musicbox_webclient/static/js/functionsvars.js +++ b/mopidy_musicbox_webclient/static/js/functionsvars.js @@ -204,12 +204,14 @@ function getAlbum (pl) { function artistsToString (artists, max) { var result = '' max = max || 3 - for (var i = 0; i < artists.length && i < max; i++) { - if (artists[i].name) { - if (i > 0) { - result += ', ' + if (artists && artists.length > 0) { + for (var i = 0; i < artists.length && i < max; i++) { + if (artists[i].name) { + if (i > 0) { + result += ', ' + } + result += artists[i].name } - result += artists[i].name } } return result @@ -578,6 +580,13 @@ function isSpotifyStarredPlaylist (playlist) { return (starredRegex.test(playlist.uri) && playlist.name === 'Starred') } +// Returns a string where {x} in template is replaced by tokens[x]. +function stringFromTemplate (template, tokens) { + return template.replace(/{[^}]+}/g, function (match) { + return tokens[match.slice(1, -1)] + }) +} + /** * Converts a URI to a jQuery-safe identifier. jQuery identifiers need to be * unique per page and cannot contain special characters. diff --git a/mopidy_musicbox_webclient/static/js/gui.js b/mopidy_musicbox_webclient/static/js/gui.js index c61acd5..682df9e 100644 --- a/mopidy_musicbox_webclient/static/js/gui.js +++ b/mopidy_musicbox_webclient/static/js/gui.js @@ -67,9 +67,9 @@ function resizeMb () { */ } -function setSongTitle (title, refresh_ui) { - songdata.track.name = title - $('#modalname').html(title) +function setSongTitle (track, refresh_ui) { + songdata.track.name = track.name + $('#modalname').html('' + track.name + '') if (refresh_ui) { resizeMb() } @@ -98,7 +98,7 @@ function setSongInfo (data) { songdata = data - setSongTitle(data.track.name, false) + setSongTitle(data.track, false) songlength = Infinity if (!data.track.length || data.track.length === 0) { @@ -131,6 +131,12 @@ function setSongInfo (data) { $('#modalalbum').html('') } images.setAlbumImage(data.track.uri, '#infocover, #albumCoverImg', mopidy) + if (data.track.uri) { + // Add 'Show Info' icon to album image + $('#modalinfo').append( + '' + + '') + } $('#modalartist').html(arttmp) @@ -190,10 +196,8 @@ function popupTracks (e, listuri, trackuri, tlid) { var divid = hash[0].substr(1) var popupName = '' if (divid === 'current') { - $('.addqueue').hide() popupName = '#popupQueue' } else { - $('.addqueue').show() popupName = '#popupTracks' } @@ -316,7 +320,8 @@ function initSocketevents () { }) mopidy.on('event:streamTitleChanged', function (data) { - setSongTitle(data.title, true) + // Update all track info. + mopidy.playback.getCurrentTlTrack().then(processCurrenttrack, console.error) }) } diff --git a/mopidy_musicbox_webclient/static/js/library.js b/mopidy_musicbox_webclient/static/js/library.js index 4c132e5..f02bb27 100644 --- a/mopidy_musicbox_webclient/static/js/library.js +++ b/mopidy_musicbox_webclient/static/js/library.js @@ -126,19 +126,12 @@ $('#searchtracks').show() } - // Returns a string where {x} in template is replaced by tokens[x]. - function theme (template, tokens) { - return template.replace(/{[^}]+}/g, function (match) { - return tokens[match.slice(1, -1)] - }) - } - - // 'Show more' pattern - var showMorePattern = '
      • Show {count} more
      • ' + // 'Show more' template + var showMoreTemplate = '
      • Show {count} more
      • ' // Artist results var child = '' - var pattern = '
      • {name}
      • ' + var template = '
      • {name}
      • ' var tokens for (i = 0; i < results.artists.length; i++) { @@ -150,11 +143,11 @@ // Add 'Show all' item after a certain number of hits. if (i === 4 && results.artists.length > 5) { - child += theme(showMorePattern, {'count': results.artists.length - i}) - pattern = pattern.replace('
      • ', '
      • ') + child += stringFromTemplate(showMoreTemplate, {'count': results.artists.length - i}) + template = template.replace('
      • ', '
      • ') } - child += theme(pattern, tokens) + child += stringFromTemplate(template, tokens) } // Inject list items, refresh listview and hide superfluous items. @@ -162,10 +155,10 @@ // Album results child = '' - pattern = '
      • ' - pattern += '
        {albumName}
        ' - pattern += '

        {artistName}

        ' - pattern += '
      • ' + template = '
      • ' + template += '
        {albumName}
        ' + template += '

        {artistName}

        ' + template += '
      • ' for (i = 0; i < results.albums.length; i++) { tokens = { @@ -187,11 +180,11 @@ } // Add 'Show all' item after a certain number of hits. if (i === 4 && results.albums.length > 5) { - child += theme(showMorePattern, {'count': results.albums.length - i}) - pattern = pattern.replace('
      • ', '
      • ') + child += stringFromTemplate(showMoreTemplate, {'count': results.albums.length - i}) + template = template.replace('
      • ', '
      • ') } - child += theme(pattern, tokens) + child += stringFromTemplate(template, tokens) } // Inject list items, refresh listview and hide superfluous items. $(SEARCH_ALBUM_TABLE).html(child).listview('refresh').find('.overflow').hide() diff --git a/mopidy_musicbox_webclient/static/mb.appcache b/mopidy_musicbox_webclient/static/mb.appcache index e0f4068..8d22e00 100644 --- a/mopidy_musicbox_webclient/static/mb.appcache +++ b/mopidy_musicbox_webclient/static/mb.appcache @@ -1,6 +1,6 @@ CACHE MANIFEST -# 2017-01-18:v2 +# 2017-01-29:v2 NETWORK: * diff --git a/tests/js/test_controls.js b/tests/js/test_controls.js index 61a699f..b181f61 100644 --- a/tests/js/test_controls.js +++ b/tests/js/test_controls.js @@ -363,12 +363,21 @@ describe('controls', function () { describe('#showInfoPopup()', function () { var track - var popup = $('
        ') + var popup = $('
        ') before(function () { track = { 'uri': QUEUE_TRACKS[0].uri, - 'length': 61000 + 'length': 61000, + 'artists': [ + { + 'uri': 'artistUri1', + 'name': 'nameMock1' + }, { + 'uri': 'artistUri2', + 'name': 'nameMock2' + } + ] } var library = { lookup: sinon.stub() @@ -377,7 +386,7 @@ describe('controls', function () { mopidy.library.lookup.returns($.when({'track:tlTrackMock1': [track]})) $(document.body).append(popup) - $('#popupTracks').data(track, track.uri) // Simulate selection from context menu + $('#popupShowInfo').data(track, track.uri) // Simulate selection from context menu $('#popupShowInfo').popup() // Initialize popup }) @@ -386,21 +395,23 @@ describe('controls', function () { }) it('should default track name', function () { - popup.append('') - controls.showInfoPopup('#popupTracks', mopidy) - assert.equal($('#name-cell').text(), '(Not available)') + controls.showInfoPopup('', '#popupShowInfo', mopidy) + assert.equal($('td:contains("Name:")').siblings('td').text(), '(Not available)') }) it('should default album name', function () { - popup.append('') - controls.showInfoPopup('#popupTracks', mopidy) - assert.equal($('#album-cell').text(), '(Not available)') + controls.showInfoPopup('', '#popupShowInfo', mopidy) + assert.equal($('td:contains("Album:")').siblings('td').text(), '(Not available)') }) it('should add leading zero if seconds length < 10', function () { - popup.append('') - controls.showInfoPopup('#popupTracks', mopidy) - assert.equal($('#length-cell').text(), '1:01') + controls.showInfoPopup('', '#popupShowInfo', mopidy) + assert.equal($('td:contains("Length:")').siblings('td').text(), '1:01') + }) + + it('should show plural for artist name', function () { + controls.showInfoPopup('', '#popupShowInfo', mopidy) + assert.isOk($('td:contains("Artists:")')) }) }) })