/** * @author Wouter van Wijk * * all kinds functions and vars */ var mopidy; //values for controls var play = false; var random; var repeat; var consume; var single; var currentVolume = -1; var mute; var volumeChanging = false; var posChanging = false; var posTimer; var volumeTimer; var seekTimer; var initgui = true; var currentpos = 0; var popupData = {}; var songlength = 0; var artistshtml = ''; var artiststext = ''; var songname = ''; var songdata = {'track': {}, 'tlid': -1}; var newposition = 0; var playlisttracksScroll; var playlistslistScroll; var STREAMS_PLAYLIST_NAME = '[Radio Streams]'; var STREAMS_PLAYLIST_SCHEME = 'm3u'; var uriSchemes = {}; //array of cached playlists (not only user-playlists, also search, artist, album-playlists) var playlists = {}; var currentplaylist; var customTracklists = []; var browseStack = []; var browseTracks = []; var ua = navigator.userAgent, isMobileSafari = /Mac/.test(ua) && /Mobile/.test(ua), isMobileWebkit = /WebKit/.test(ua) && /Mobile/.test(ua), isMobile = /Mobile/.test(ua), isWebkit = /WebKit/.test(ua); //constants PROGRAM_NAME = 'MusicBox'; ARTIST_TABLE = '#artiststable'; ALBUM_TABLE = '#albumstable'; PLAYLIST_TABLE = '#playlisttracks'; CURRENT_PLAYLIST_TABLE = '#currenttable'; SEARCH_ALL_TABLE = '#allresulttable'; SEARCH_ALBUM_TABLE = '#albumresulttable'; SEARCH_ARTIST_TABLE = '#artistresulttable'; SEARCH_TRACK_TABLE = '#trackresulttable'; URI_SCHEME = 'mbw'; PLAY_NOW = 0; PLAY_NEXT = 1; ADD_THIS_BOTTOM = 2; ADD_ALL_BOTTOM = 3; PLAY_ALL = 4; PLAY_NOW_SEARCH = 5; MAX_TABLEROWS = 50; //update track slider timer, milliseconds TRACK_TIMER = 1000; //check status timer, every 5 or 10 sec STATUS_TIMER = 10000; // the first part of Mopidy extensions which serve radio streams var radioExtensionsList = ['somafm', 'tunein', 'dirble', 'audioaddict']; var uriClassList = [ ['spotify', 'fa-spotify'], ['spotifytunigo', 'fa-spotify'], ['local', 'fa-file-sound-o'], ['m3u', 'fa-file-sound-o'], ['podcast', 'fa-rss-square'], ['dirble', 'fa-microphone'], ['tunein', 'fa-headphones'], ['soundcloud', 'fa-soundcloud'], ['sc', 'fa-soundcloud'], ['gmusic', 'fa-google'], ['internetarchive', 'fa-university'], ['somafm', 'fa-flask'], ['youtube', 'fa-youtube'], ['yt', 'fa-youtube'], ['audioaddict', 'fa-bullhorn'], ['subsonic', 'fa-folder-open'] ]; var uriHumanList = [ ['spotify', 'Spotify'], ['spotifytunigo', 'Spotify Browse'], ['local', 'Local Files'], ['m3u', 'Local Playlists'], ['podcast', 'Podcasts'], ['dirble', 'Dirble'], ['tunein', 'TuneIn'], ['soundcloud', 'SoundCloud'], ['gmusic', 'Google Music'], ['internetarchive', 'Internet Archive'], ['somafm', 'Soma FM'], ['youtube', 'YouTube'], ['audioaddict', 'AudioAddict'], ['subsonic', 'Subsonic'] ]; function scrollToTop() { var divtop = 0; $('body,html').animate({ scrollTop: divtop }, 250); } function scrollToTracklist() { var divtop = $("#playlisttracksdiv").offset().top - 120; $('body,html').animate({ scrollTop: divtop }, 250); } //A hack to find the name of the first artist of a playlist. this is not yet returned by mopidy //does not work wel with multiple artists of course function getArtist(pl) { for (var i = 0; i < pl.length; i++) { for (var j = 0; j < pl[i].artists.length; j++) { if (pl[i].artists[j].name != '') { return pl[i].artists[j].name; } } }; } //A hack to find the first album of a playlist. this is not yet returned by mopidy function getAlbum(pl) { for (var i = 0; i < pl.length; i++) { if (pl[i].album.name != '') { return pl[i].album.name; } }; } 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 += ', '; } result += artists[i].name; } } return result; } /******************************************************** * break up results and put them in album tables *********************************************************/ function albumTracksToTable(pl, target, uri) { var tmp = ''; $(target).html(tmp); $(target).attr('data', uri); } function renderSongLi(song, liID, uri) { var name; if (!song.name || song.name == '') { name = uri.split('/'); name = decodeURI(name[name.length - 1]); } else { name = song.name; } songLi = '
  • ' + '' + '' + '' + '

    ' + name + '

    ' + '
  • '; return songLi; } function renderQueueSongLi(song, liID, uri, tlid) { var name; if (!song.name || song.name == '') { name = uri.split('/'); name = decodeURI(name[name.length - 1]); } else { name = song.name; } songLi = '
  • ' + '' + '' + '' + '

    ' + name + '

    ' + '
  • '; return songLi; } function resultsToTables(results, target, uri) { if (!results) { return } var tlids = []; if (target == CURRENT_PLAYLIST_TABLE) { for (i = 0; i < results.length; i++) { tlids[i] = results[i].tlid; results[i] = results[i].track; } } var newalbum = []; var newtlids = []; //keep a list of albums for retreiving of covers var coversList = []; var nextname = ''; var count = 0; $(target).html(''); //break into albums and put in tables var html = ''; var tableid, j, artistname, alburi, name, iconClass; var targetmin = target.substr(1); $(target).attr('data', uri); var length = 0 || results.length; for (i = 0; i < length; i++) { //create album if none extists if (!results[i].album) { results[i].album = {"__model__": "Album"}; } //create album uri if there is none if (!results[i].album.uri) { results[i].album.uri = 'x'; } if (!results[i].album.name) { results[i].album.name = ''; } //create name if there is no one if (!results[i].name || results[i].name == '') { name = results[i].uri.split('/'); results[i].name = decodeURI(name[name.length - 1]) || 'Track ' + String(i); } //leave out unplayable items if (results[i].name.substring(0, 12) == '[unplayable]') continue; newalbum.push(results[i]); newtlids.push(tlids[i]); nextname = ''; if ((i < length - 1) && results[i + 1].album && results[i + 1].album.name) { nextname = results[i + 1].album.name; } if (results[i].length == -1) { html += '
  • ' + results[i].name + ' [Stream]

  • '; newalbum = []; newtlids = []; nextname = ''; } else { if ((results[i].album.name != nextname) || (nextname == '')) { tableid = 'art' + i; //render differently if only one track in the album if (newalbum.length == 1) { if (i != 0) { html += '
  •  
  • '; } iconClass = getMediaClass(newalbum[0].uri); var liID = targetmin + '-' + newalbum[0].uri; if (target == CURRENT_PLAYLIST_TABLE) { html += '
  • ' + '' + '' + '' + '

    ' + newalbum[0].name + "

    "; } else { html += '

  • ' + '' + '' + '' + '

    ' + newalbum[0].name + "

    "; } if (newalbum[0].artists) { for (j = 0; j < newalbum[0].artists.length; j++) { html += newalbum[0].artists[j].name; html += (j == newalbum[0].artists.length - 1) ? '' : ' / '; //stop after 3 if (j > 2) { html += '...'; break; } } } if (newalbum[0].album.name != '') { html += ' / '; } html += '' + newalbum[0].album.name + '

    '; html += '
  • '; popupData[newalbum[0].uri] = newalbum[0]; newalbum = []; newtlids = []; } else { //newalbum length if (results[i].album.uri && results[i].album.name) { iconClass = getMediaClass(newalbum[0].uri); html += '
  • '; html += '

    ' + results[i].album.name + '

    '; } if (results[i].album.artists) { for (j = 0; j < results[i].album.artists.length; j++) { html += results[i].album.artists[j].name; html += (j == results[i].album.artists.length - 1) ? '' : ' / '; //stop after 3 if (j > 2) { child += '...'; break; } } } html += '

  • '; for (j = 0; j < newalbum.length; j++) { popupData[newalbum[j].uri] = newalbum[j]; //hERE! var liID = targetmin + '-' + newalbum[j].uri; if (target == CURRENT_PLAYLIST_TABLE) { html += renderQueueSongLi(newalbum[j], liID, uri, newtlids[j]); } else { html += renderSongLi(newalbum[j], liID, uri); } } newalbum = []; newtlids = []; if (results[i].album) { coversList.push([results[i].album, i]); } } //newalbum length if (results[i].album) { coversList.push([results[i].album, i]); } } //albums name } } tableid = "#" + tableid; $(target).html(html); $(target).attr('data', uri); //retreive albumcovers for (i = 0; i < coversList.length; i++) { getCover(coversList[i][0], target + '-cover-' + coversList[i][1], 'small'); } } //process updated playlist to gui function playlisttotable(pl, target, uri) { var tmp = ''; $(target).html(''); var targetmin = target.substr(1); var child = ''; for (var i = 0; i < pl.length; i++) { if (pl[i]) { popupData[pl[i].uri] = pl[i]; child = '
  • '; child += '

    ' + pl[i].name + "

    "; child += '

    '; child += '' + timeFromSeconds(pl[i].length / 1000) + ''; for (var j = 0; j < pl[i].artists.length; j++) { if (pl[i].artists[j]) { child += pl[i].artists[j].name; child += (j == pl[i].artists.length - 1) ? '' : ' / '; //stop after 3 if (j > 2) { child += '...'; break; } } } child += ' / ' + pl[i].album.name + '

    '; child += '
  • '; tmp += child; } }; $(target).html(tmp); $(target).attr('data', uri); } function getPlaylistTracks(uri) { if (playlists[uri] && playlists[uri].tracks) { return Mopidy.when(playlists[uri].tracks); } else { showLoading(true); return mopidy.playlists.getItems({'uri': uri}).then(function(refs) { return processPlaylistItems({'uri': uri, 'items': refs}); }, console.error); } } function getUris(tracks) { var results = []; for (var i = 0; i < tracks.length; i++) { results.push(tracks[i].uri); } return results; } function getTracksFromUri(uri, full_track_data) { var returnTracksOrUris = function(tracks) { return (full_track_data || false) ? tracks : getUris(tracks); } if (customTracklists[uri]) { return returnTracksOrUris(customTracklists[uri]); } else if (playlists[uri] && playlists[uri].tracks) { return returnTracksOrUris(playlists[uri].tracks); } return []; } //convert time to human readable format function timeFromSeconds(length) { var d = Number(length); var h = Math.floor(d / 3600); var m = Math.floor(d % 3600 / 60); var s = Math.floor(d % 3600 % 60); return ((h > 0 ? h + ":" : "") + (m > 0 ? (h > 0 && m < 10 ? "0" : "") + m + ":" : "0:") + (s < 10 ? "0" : "") + s); } /******* Toast ***/ function toast(message, delay, textOnly) { textOnl = textOnly || false; message = message || "Loading..."; delay = delay || 1000; $.mobile.loading('show', { text: message, textVisible: true, theme: 'a', textonly: textOnl }); if (delay > 0) { setTimeout(function() { $.mobile.loading('hide'); }, delay); } } /****************** * Modal dialogs * ******************/ function showLoading(on) { if (on) { $("body").css("cursor", "progress"); $.mobile.loading('show', { text: 'Loading data from ' + PROGRAM_NAME + '. Please wait...', textVisible: true, theme: 'a' }); } else { $("body").css("cursor", "default"); $.mobile.loading('hide'); } } function showOffline(on) { if (on) { $.mobile.loading('show', { text: 'Trying to reach ' + PROGRAM_NAME + '. Please wait...', textVisible: true, theme: 'a' }); } else { $.mobile.loading('hide'); } } // from http://dzone.com/snippets/validate-url-regexp function validUri(str) { var regexp = /^(mms|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/ return regexp.test(str); } function validServiceUri(str) { return validUri(str) || isServiceUri(str); } function getScheme(uri) { return uri.split(':')[0].toLowerCase(); } function isStreamUri(uri) { var a = validUri(uri); var b = radioExtensionsList.indexOf(getScheme(uri)) >= 0; return a || b; } function getMediaClass(uri) { var scheme = getScheme(uri); for (var i = 0; i < uriClassList.length; i++) { if (scheme == uriClassList[i][0]) { return "fa " + uriClassList[i][1]; } } return ''; } function getMediaHuman(uri) { var scheme = getScheme(uri); for (var i = 0; i < uriHumanList.length; i++) { if (scheme == uriHumanList[i][0]) { return uriHumanList[i][1]; } } return ''; } function isServiceUri(uri) { var scheme = getScheme(uri); for (var i = 0; i < uriClassList.length; i++) { if (scheme == uriClassList[i][0]) { return true; } } for (var i = 0; i < radioExtensionsList.length; i++) { if (scheme == radioExtensionsList[i]) { return true; } } return false; } function isFavouritesPlaylist(playlist) { return (playlist.name == STREAMS_PLAYLIST_NAME && getScheme(playlist.uri) == STREAMS_PLAYLIST_SCHEME); } function isSpotifyStarredPlaylist(playlist) { var starredRegex = /spotify:user:.*:starred/g; return (starredRegex.test(playlist.uri) && playlist.name == 'Starred'); }