diff --git a/server/djrandom/frontend/static/player.js b/server/djrandom/frontend/static/player.js index 84ad215d411db9829a13aa0e7f053a20a162a9f7..e0aa3a8e2900e1014bf19868d28ce9c86e27acd8 100644 --- a/server/djrandom/frontend/static/player.js +++ b/server/djrandom/frontend/static/player.js @@ -8,14 +8,151 @@ djr.generateRandomId = function() { return uuid.join(''); }; +// Backend API stub. -djr.Player = function(selector) { +djr.Backend = function(userid) { + this.userid = userid; +}; + +// Read a playlist, calls callback(Playlist). +djr.Backend.prototype.getPlaylist(uuid, callback, ctx) { + $.ajax({url: '/json/playlist/get/' + uuid, + dataType: 'json', + context: ctx, + success: function(data, status, jqxhr) { + callback(new djr.PlaylistChunk(data.songs)); + } + }); +}; + +// Save a playlist (song array). +djr.Backend.prototype.savePlaylist(uuid, songs) { + $.ajax({url: '/json/playlist/save', + data: {uuid: uuid, + h: songs.join(',')}, + type: 'POST' + }); +}; + +// Return the HTML fragment for an array of songs. +djr.Backend.prototype.getHtmlForSongs(songs, callback, ctx) { + $.ajax({url: '/fragment/songs', + data: {'h': songs.join(',')}, + dataType: 'html', + type: 'POST', + context: ctx || this, + success: callback, + }); +}; + +djr.Backend.prototype.nowPlaying = function(song) { + $.getJSON('/json/playing/' + song); +}; + + + +// A Playlist chunk (basically, an array of songs). + +djr.PlaylistChunk = function(songs) { + this.songs = songs || []; +}; + +djr.PlaylistChunk.prototype.hasSong(sha1) { + return (this.songs.indexOf(sha1) >= 0); +}; + +djr.PlaylistChunk.prototype.removeSong(sha1) { + this.songs = $.grep(this.songs, function(a) { return a != sha1; }); +}; + + + +// Playlist. + +djr.Playlist = function() { + this.uuid = null; + this.chunks = []; + this.song_map = {}; + this.chunk_map = {}; + this.next_chunk_id = 0; +}; + +// Return an array with all songs, in order. +djr.Playlist.prototype.allSongs = function() { + var songs = []; + $.each(this.chunks, function(idx, chunk_id) { + $.each(this.chunk_map[chunk_id].songs, function(idx2, song) { + songs.push(song); + })}); + return songs; +}; + +// Add a new chunk (only adds unique songs). +// Returns the chunk ID. +djr.Playlist.prototype.addChunk = function(playlist_chunk) { + chunk_id = this.next_chunk_id++; + var songs = []; + $.each(playlist_chunk.songs, function(idx, song) { + if (this.song_map[song]) { + continue; + } + songs.push(song); + this.song_map[song] = chunk_id; + }); + this.chunk_map[chunk_id] = new PlaylistChunk(songs); + this.chunks.push(chunk_id); + return chunk_id; +}; + +// Remove a chunk (by ID). +djr.Playlist.prototype.removeChunk = function(chunk_id) { + $.each(this.chunks[chunk_id].songs, function(idx, song){ + delete this.song_map[song]; + }); + delete this.chunk_map[chunk_id]; + this.chunks = $.grep(this.chunks, function(cid) { return cid != chunk_id; }); +}; + +// Return a new Playlist with all the current chunks merged. +djr.Playlist.prototype.merge = function() { + var new_playlist = new djr.Playlist(); + new_playlist.uuid = this.uuid; + new_playlist.addChunk(this.allSongs()); + return new_playlist; +}; + +// Find the next song. +djr.Playlist.prototype.getNextSong = function(song) { + var cur_chunk = this.song_map[song]; + var chunk_songs = this.chunk_map[cur_chunk].songs; + + var chunk_idx = this.chunks.indexOf(cur_chunk); + var idx = chunk_songs.indexOf(song) + 1; + + if (idx >= chunk_songs.length) { + idx = 0; + chunk_idx++; + if (chunk_idx >= this.chunks.length) { + chunk_idx = 0; + } + } + + return this.chunk_map[this.chunks[chunk_idx]].songs[idx]; +}; + + + + + +// Player + +djr.Player = function(backend, selector) { + this.backend = backend; this.player = $(selector); - this.curPlaylist = Array(); - this.curPlaylistId = null; - this.curIdx = -1; - this.curSong = null; + this.playlist = new djr.Playlist(); + this.cur_song = null; + // Setup the jPlayer interface. this.player.jPlayer({ ready: function() {}, }); @@ -23,8 +160,102 @@ djr.Player = function(selector) { function () {djr.state.player.nextSong(); }); this.player.bind($.jPlayer.event.error + '.djr', function() {djr.state.player.reportError(); }); + +}; + +djr.Player.prototype.hideAllChunks = function() { + $('.chunk .inner').hide(); +}; + +// Callback for removechunk click. +djr.Player.prototype.removeChunkCallback = function() { + var chunk_id = $(this).attr('id').substr(6); + this.playlist.removeChunk(chunk_id); + $(this).remove(); }; +// Search! +djr.Player.prototype.search = function(query) { + this.backend.search(query, function(results) { + var songs = []; + $.each(results, function(idx, item) { + songs.push(item.sha1); + }); + + var chunk_id = this.playlist.addChunk(new PlaylistChunk(songs)); + + var player = this; + this.backend.getHtmlForSongs(songs, function(songs_html) { + var chunk_div = 'chunk_' + chunk_id; + player.hideAllChunks(); + $('#playlistDiv').append( + '<div id=\"' + chunk_div + '\" class=\"chunk\">' + + '<div class=\"chunk_title\">' + query + '</div>' + + '<div class=\"chunk_inner\">' + + songs_html + '</div></div>'); + $('#' + chunk_div + ' .song_a').click(function() { + player.play($(this).attr('id').substr(5)); + }); + $('#' + chunk_div + ' .album_a').click(function() { + player.search('(album:\"' + $(this).text() + '\")'); + }); + }, this); + }, this); + +}; + +// Start playing a specific song. +djr.Player.prototype.play = function(song) { + djr.debug('play ' + song); + + this.cur_song = song; + + $('.song').removeClass('playing'); + $('#song_' + song).addClass('playing'); + + var url = '/dl/' + song; + this.player.jPlayer('setMedia', + {mp3: url}).jPlayer('play'); + + // Report the new song being played. + this.backend.nowPlaying(song); +}; + +// Start playing the next song. +djr.Player.prototype.nextSong = function() { + this.play(this.playlist.getNextSong(this.cur_song)); +}; + + + +djr = {}; + +djr.state = { + backend: null, + player: null, + userid: null +}; + +djr.init = function (userid) { + djr.state.userid = userid; + djr.state.backend = new djr.Backend(userid); + djr.state.player = new Player(djr.state.backend, '#djr_player'); +}; + +// Export the player for quick onclick access +djr.player = function() { + return djr.state.player; +}; + +// Debugging. +djr.debug = function(msg) { + $('#debug').append(msg + '<br>'); +}; + + + +// ------ OLD -------- + // Return the current playlist ID. djr.Player.prototype.getCurrentPlaylistID = function() { return this.curPlaylistId; @@ -54,20 +285,6 @@ djr.Player.prototype.setPlaylist = function(hashes) { this.curIdx = -1; }; -// Add songs to the playlist, do not add songs already in there. -djr.Player.prototype.extendPlaylist = function(hashes) { - var i, map = {}; - for (i = 0; i < this.curPlaylist.length; i++) { - map[this.curPlaylist[i]] = 1; - } - for (i = 0; i < hashes.length; i++) { - if (map[hashes[i]]) { - continue; - } - this.curPlaylist.push(hashes[i]); - map[hashes[i]] = 1; - } -}; // Save the current playlist. djr.Player.prototype.savePlaylist = function() { @@ -75,11 +292,7 @@ djr.Player.prototype.savePlaylist = function() { if (!this.curPlaylistId) { return; } - $.ajax({url: '/json/playlist/save', - data: {uuid: this.curPlaylistId, - h: this.curPlaylist.join(',')}, - type: 'POST' - }); + this.backend.savePlaylist(this.curPlaylistId, this.curPlaylist); }; // Load a playlist. @@ -124,22 +337,6 @@ djr.Player.prototype.nextSong = function() { this.playSong(next_sha1); }; -// Start playing a specific song. -djr.Player.prototype.playSong = function(sha1) { - this.curSong = sha1; - this.curIdx = $.inArray(sha1, this.curPlaylist); - - $('.song').removeClass('playing'); - $('#song_' + sha1).addClass('playing'); - - var url = '/dl/' + sha1; - djr.debug('playing ' + url); - this.player.jPlayer('setMedia', - {mp3: url}).jPlayer('play'); - - // Report the new song being played. - $.getJSON('/json/playing/' + sha1); -}; // An error has occurred in the player. djr.Player.prototype.reportError = function(event) { diff --git a/server/djrandom/frontend/templates/_base.html b/server/djrandom/frontend/templates/_base.html index bf07cd42fadbf9972b7e63820140af123110b173..3512ccb30f4ba1150c23bf6c651b78c7d3fb1281 100644 --- a/server/djrandom/frontend/templates/_base.html +++ b/server/djrandom/frontend/templates/_base.html @@ -15,9 +15,7 @@ <script type="text/javascript" src="/static/js/jquery.jplayer.min.js"></script> <script type="text/javascript" - src="/static/djrandom.js?v=4"></script> - <script type="text/javascript" - src="/static/player.js?v=4"></script> + src="/static/player.js?v=5"></script> {% block head %}{% endblock %} </head> <body>