diff --git a/server/djrandom/frontend/api_views.py b/server/djrandom/frontend/api_views.py index b8a8dbbb7bb47d3b9f73da88c8c5e3eee5dba7b3..e4cf6aec432a7cfe9466bd68738c235d354ef530 100644 --- a/server/djrandom/frontend/api_views.py +++ b/server/djrandom/frontend/api_views.py @@ -165,11 +165,19 @@ def last_uploaded_json(): return jsonify(results=last_uploaded) -@app.route('/json/random', methods=['POST']) +@app.route('/json/markov', methods=['POST']) @require_auth -def random_json(): +def markov_json(): n = int(request.form.get('n', 10)) hashes = request.form.get('h', '').split(',') last_song = hashes[-1] sequence = svcs['markov'].generate_sequence(last_song, 2, n) return jsonify(results=sequence) + + +@app.route('/json/random', methods=['GET']) +@require_auth +def random_json(): + n = int(request.args.get('n', 10)) + random_songs = [x.sha1 for x in MP3.get_random_songs(n)] + return jsonify(results=random_songs) diff --git a/server/djrandom/frontend/static/js/djr.min.js b/server/djrandom/frontend/static/js/djr.min.js index ab4ad99916aaf9e15ce36378cb4d45f69f642ee2..9b3bb309c3dab906692d8d464fa17ba46f74f02c 100644 --- a/server/djrandom/frontend/static/js/djr.min.js +++ b/server/djrandom/frontend/static/js/djr.min.js @@ -1,7 +1,7 @@ djr={};var CHARS="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split("");djr.generateRandomId=function(){var a=[],b=CHARS,c,d=b.length;for(c=0;c<40;c++)a[c]=b[0|Math.random()*d];return a.join("")};djr.Backend=function(){};djr.Backend.prototype.search=function(a,b,c){$.ajax({url:"/json/search",data:{q:a},dataType:"json",type:"GET",context:c||this,success:function(a){b(a.results)}})};djr.Backend.prototype.moreLikeThese=function(a,b,c){$.ajax({url:"/json/morelikethese",data:{h:a.join(",")},dataType:"json",type:"POST",context:c||this,success:function(a){b(a.results)}})}; djr.Backend.prototype.getPlaylist=function(a,b,c){$.ajax({url:"/json/playlist/get/"+a,dataType:"json",type:"GET",context:c||this,success:function(c){b(new djr.PlaylistChunk(c.songs,a))}})};djr.Backend.prototype.savePlaylist=function(a,b){$.ajax({url:"/json/playlist/save",data:{uuid:a,h:b.join(",")},type:"POST"})};djr.Backend.prototype.streamPlaylist=function(a,b,c,d){$.ajax({url:"/json/playlist/stream",data:{uuid:a,stream:b?"y":"n"},dataType:"json",type:"POST",context:d||this,success:function(a){c(a)}})}; djr.Backend.prototype.getHtmlForSongs=function(a,b,c){$.ajax({url:"/fragment/songs",data:{h:a.join(",")},dataType:"html",type:"POST",context:c||this,success:b})};djr.Backend.prototype.nowPlaying=function(a,b){$.ajax({url:"/json/playing",data:{cur:a,prev:b.join(",")},type:"POST"})};djr.Backend.prototype.mostPlayedPlaylist=function(a,b,c){$.ajax({url:"/json/most_played",data:{n:a},dataType:"json",type:"GET",context:c||this,success:function(a){b(a.results)}})}; -djr.Backend.prototype.lastPlaylist=function(a,b,c){$.ajax({url:"/json/last_uploaded",data:{n:a},dataType:"json",type:"GET",context:c||this,success:function(a){b(a.results)}})};djr.Backend.prototype.randomPlaylist=function(a,b,c,d){$.ajax({url:"/json/random",data:{h:b.join(","),n:a},dataType:"json",type:"POST",context:d||this,success:function(a){c(a.results)}})};djr.PlaylistChunk=function(a,b){this.songs=a||[];this.title=b};djr.PlaylistChunk.prototype.hasSong=function(a){return this.songs.indexOf(a)>=0};djr.PlaylistChunk.prototype.removeSong=function(a){this.songs=$.grep(this.songs,function(b){return b!=a})};djr.controlButtons=function(a){return'<div class="ctlbox" style="display:none"><a id="'+a+'_remove" class="ctl_btn ctl_remove"> </a></div>'}; +djr.Backend.prototype.lastPlaylist=function(a,b,c){$.ajax({url:"/json/last_uploaded",data:{n:a},dataType:"json",type:"GET",context:c||this,success:function(a){b(a.results)}})};djr.Backend.prototype.markovPlaylist=function(a,b,c,d){$.ajax({url:"/json/markov",data:{h:b.join(","),n:a},dataType:"json",type:"POST",context:d||this,success:function(a){c(a.results)}})};djr.Backend.prototype.randomPlaylist=function(a,b,c){$.ajax({url:"/json/random",data:{n:a},dataType:"json",type:"GET",context:c||this,success:function(a){b(a.results)}})};djr.PlaylistChunk=function(a,b){this.songs=a||[];this.title=b};djr.PlaylistChunk.prototype.hasSong=function(a){return this.songs.indexOf(a)>=0};djr.PlaylistChunk.prototype.removeSong=function(a){this.songs=$.grep(this.songs,function(b){return b!=a})};djr.controlButtons=function(a){return'<div class="ctlbox" style="display:none"><a id="'+a+'_remove" class="ctl_btn ctl_remove"> </a></div>'}; djr.PlaylistChunk.prototype.wrapHtml=function(a,b){return'<div id="chunk_'+a+'" class="chunk"><div class="chunk_ctl_wrap">'+djr.controlButtons("chunk_ctl_"+a)+'<a class="chunk_title">'+this.title+'</a></div><div class="chunk_inner">'+(b||"")+"</div></div>"};djr.Playlist=function(a){this.uuid=a||djr.generateRandomId();this.chunks=[];this.song_map={};this.chunk_map={};this.next_chunk_id=0}; djr.Playlist.prototype.allSongs=function(){var a=[],b,c;for(b=0;b<this.chunks.length;b++)for(c=0;c<this.chunk_map[this.chunks[b]].songs.length;c++)a.push(this.chunk_map[this.chunks[b]].songs[c]);return a};djr.Playlist.prototype.createUniqueChunk=function(a,b){var c=[],d;for(d=0;d<a.length;d++)this.song_map[a[d]]==null&&c.push(a[d]);return c.length>0?new djr.PlaylistChunk(c,b):null}; djr.Playlist.prototype.addChunk=function(a){djr.debug("adding chunk to playlist "+this.uuid);var b,c=this.next_chunk_id++;for(b=0;b<a.songs.length;b++)this.song_map[a.songs[b]]=c;this.chunk_map[c]=a;this.chunks.push(c);return c};djr.Playlist.prototype.getChunkSongs=function(a){return this.chunk_map[a].songs}; @@ -9,8 +9,8 @@ djr.Playlist.prototype.removeChunk=function(a){djr.debug("removing chunk "+a);va djr.Playlist.prototype.merge=function(){var a=[],b;for(b=0;b<this.chunks.length;b++)a.push(this.chunk_map[this.chunks[b]].title);a=a.join(" + ");b=new djr.Playlist;b.uuid=this.uuid;b.addChunk(new djr.PlaylistChunk(this.allSongs(),a));return b};djr.Playlist.prototype.getNextSong=function(a){var b=this.song_map[a],c=this.chunk_map[b].songs,b=this.chunks.indexOf(b),a=c.indexOf(a)+1;a>=c.length&&(a=0,b++,b>=this.chunks.length&&(b=0));return this.chunk_map[this.chunks[b]].songs[a]};djr.Player=function(a,b){this.backend=a;this.player=$(b);this.playlist=new djr.Playlist;this.old_songs=[];this.cur_song=null;this.player.jPlayer({swfPath:"/static/js",ready:function(){djr.debug("player ready")}});this.player.bind($.jPlayer.event.ended+".djr",function(){djr.state.player.nextSong()});this.player.bind($.jPlayer.event.error+".djr",function(){djr.state.player.reportError()})};djr.Player.prototype.hideAllChunks=function(){$(".chunk .chunk_inner").hide()}; djr.Player.prototype.removeChunk=function(a){this.playlist.removeChunk(a);this.savePlaylist();$("#chunk_"+a).remove()};djr.Player.prototype.removeSong=function(a){$("#song_"+a).remove();a=this.playlist.removeSong(a);this.savePlaylist();a>0&&$("#chunk_"+a).remove()};djr.Player.prototype.savePlaylist=function(){this.backend.savePlaylist(this.playlist.uuid,this.playlist.allSongs())};djr.Player.prototype.clearPlaylist=function(){this.playlist=new djr.Playlist;$("#playlistDiv").empty()}; djr.Player.prototype.mergePlaylistChunks=function(){this.playlist=this.playlist.merge();var a=[];$(".chunk .chunk_inner").each(function(){a.push($(this).html())});$("#playlistDiv").empty();var b=this.playlist.chunks[0];this.setChunkHtml(this.playlist.chunk_map[b],b,a.join(""))};djr.Player.prototype.search=function(a){var b=this;this.backend.search(a,function(c){var d=[];$.each(c,function(a,b){d.push(b.sha1)});d.length==0?djr.debug("No results found."):b.createChunk(d,a)})}; -djr.Player.prototype.lastPlaylist=function(a){var b=this,c="Last "+a+" Songs Uploaded";this.backend.lastPlaylist(a,function(a){a.length==0?djr.debug("No results found."):b.createChunk(a,c)})};djr.Player.prototype.randomPlaylist=function(a){var b=this,c="Last "+a+" Random Songs ";this.backend.randomPlaylist(a,this.playlist.allSongs(),function(a){a.length==0?djr.debug("No results found."):b.createChunk(a,c)})}; -djr.Player.prototype.mostPlayedPlaylist=function(a){var b=this,c="Most "+a+" Played Songs";this.backend.mostPlayedPlaylist(a,function(a){var e=[];$.each(a,function(a,b){e.push(b.sha1)});e.length==0?djr.debug("No results found."):b.createChunk(e,c)})};djr.Player.prototype.extendCurrentPlaylist=function(){var a=this;this.backend.moreLikeThese(this.playlist.allSongs(),function(b){a.createChunk(b,"suggestions")})}; +djr.Player.prototype.lastPlaylist=function(a){var b=this,c="Last "+a+" Songs Uploaded";this.backend.lastPlaylist(a,function(a){a.length==0?djr.debug("No results found."):b.createChunk(a,c)})};djr.Player.prototype.randomPlaylist=function(a){var b=this,c=""+a+" Random Songs ";this.backend.randomPlaylist(a,function(a){a.length==0?djr.debug("No results found."):b.createChunk(a,c)})}; +djr.Player.prototype.mostPlayedPlaylist=function(a){var b=this,c=""+a+" Most Played Songs";this.backend.mostPlayedPlaylist(a,function(a){var e=[];$.each(a,function(a,b){e.push(b.sha1)});e.length==0?djr.debug("No results found."):b.createChunk(e,c)})};djr.Player.prototype.extendCurrentPlaylist=function(){var a=this;this.backend.moreLikeThese(this.playlist.allSongs(),function(b){a.createChunk(b,"suggestions")})}; djr.Player.prototype.createChunk=function(a,b){var c=this.playlist.createUniqueChunk(a,b);if(c){this.playlist.chunks.length>1&&this.mergePlaylistChunks();var d=this.playlist.addChunk(c);this.savePlaylist();this.backend.getHtmlForSongs(a,function(a){this.hideAllChunks();this.setChunkHtml(c,d,a)},this)}else djr.debug("All the results are already in the playlist")}; djr.Player.prototype.setChunkHtml=function(a,b,c){a=a.wrapHtml(b,c);$("#playlistDiv").append(a);var d=this,e=$("#chunk_"+b);e.find(".song_a").click(function(){d.play($(this).attr("id").substr(5))});e.find(".album_a").click(function(){d.search('(album:"'+$(this).text()+'")')});e.find(".chunk_title").click(function(){e.find(".chunk_inner").toggle()});e.hover(function(){$(this).find(".chunk_ctl_wrap .ctlbox").show()},function(){$(this).find(".chunk_ctl_wrap .ctlbox").hide()});e.find(".chunk_ctl_wrap .ctlbox .ctl_remove").click(function(){djr.debug("removing chunk "+ b);d.removeChunk(b)});e.find(".chunk_inner .song").hover(function(){$(this).find(".ctlbox").show()},function(){$(this).find(".ctlbox").hide()});e.find(".chunk_inner .ctlbox .ctl_remove").click(function(){var a=$(this).parent().parent().attr("id").substr(5);d.removeSong(a)})}; diff --git a/server/djrandom/frontend/static/js/djr/Makefile b/server/djrandom/frontend/static/js/djr/Makefile index 3075a035437c1834671fe1a5ab3dc4525c0dfb8e..7d483dbc1cde3db997329792888b3caf693ae2ba 100644 --- a/server/djrandom/frontend/static/js/djr/Makefile +++ b/server/djrandom/frontend/static/js/djr/Makefile @@ -1,5 +1,6 @@ -JSCOMPILER = java -jar /usr/bin/compiler.jar +JSCOMPILER_JAR = /usr/lib/jscompiler/compiler.jar +JSCOMPILER = java -jar $(JSCOMPILER_JAR) SOURCES = \ djr.js \ diff --git a/server/djrandom/frontend/static/js/djr/backend.js b/server/djrandom/frontend/static/js/djr/backend.js index 2bf1fcb94a4964016764c7bde2bf0464334c48f7..1921dc102228286e3ec43b720160cb30fdcc1854 100644 --- a/server/djrandom/frontend/static/js/djr/backend.js +++ b/server/djrandom/frontend/static/js/djr/backend.js @@ -199,13 +199,14 @@ djr.Backend.prototype.lastPlaylist = function(num, callback ,ctx) { }; /** - * Request N most played songs + * Return N pseudo-random songs based on the current playlist. * * @param {integer} n Number of songs requested + * @param {Array[string]} uuids SHA1 hashes of the current playlist * */ -djr.Backend.prototype.randomPlaylist = function(num, uuids, callback ,ctx) { - $.ajax({url: '/json/random', +djr.Backend.prototype.markovPlaylist = function(num, uuids, callback ,ctx) { + $.ajax({url: '/json/markov', data: {'h': uuids.join(','), 'n': num }, dataType: 'json', @@ -217,3 +218,21 @@ djr.Backend.prototype.randomPlaylist = function(num, uuids, callback ,ctx) { }); }; +/** + * Return N completely random songs. + * + * @param {integer} n Number of songs requested + * + */ +djr.Backend.prototype.randomPlaylist = function(num, callback ,ctx) { + $.ajax({url: '/json/random', + data: {'n': num }, + dataType: 'json', + type: 'GET', + context: ctx || this, + success: function(data, status, jqxhr) { + callback(data.results); + } + }); +}; + diff --git a/server/djrandom/frontend/static/js/djr/player.js b/server/djrandom/frontend/static/js/djr/player.js index 6f9ff0b34646b7a1c5e0b9d3966d14941e5c861b..6900a126b4302ea823016d7841386a1a1613585c 100644 --- a/server/djrandom/frontend/static/js/djr/player.js +++ b/server/djrandom/frontend/static/js/djr/player.js @@ -118,8 +118,8 @@ djr.Player.prototype.lastPlaylist = function(num) { djr.Player.prototype.randomPlaylist = function(num) { var player = this; - var title = "Last " + num + " Random Songs "; - this.backend.randomPlaylist(num, this.playlist.allSongs(), function(results) { + var title = "" + num + " Random Songs "; + this.backend.randomPlaylist(num, function(results) { var songs = results; if (songs.length == 0) { djr.debug('No results found.'); @@ -133,7 +133,7 @@ djr.Player.prototype.randomPlaylist = function(num) { djr.Player.prototype.mostPlayedPlaylist = function(num) { var player = this; - var title = "Most " + num + " Played Songs"; + var title = "" + num + " Most Played Songs"; this.backend.mostPlayedPlaylist(num, function(results) { var songs = []; $.each(results, function(idx, item) { diff --git a/server/djrandom/frontend/templates/_std_base.html b/server/djrandom/frontend/templates/_std_base.html new file mode 100644 index 0000000000000000000000000000000000000000..eef79cf7d907e9963a66c3b4f25464903642e1a8 --- /dev/null +++ b/server/djrandom/frontend/templates/_std_base.html @@ -0,0 +1,17 @@ +<!doctype html> +<html> + <head> + <title>DJ RANDOM :: {% block title %}{% endblock %}</title> + <link href="/static/favicon.ico" type="image/x-icon" rel="shortcut icon"> + <link rel="stylesheet" type="text/css" href="/static/css/login.css"> + </head> + <body> + + <div id="main"> + <div id="form"> + {% block main %} + {% endblock %} + </div> + </div> + </body> +</html> diff --git a/server/djrandom/frontend/templates/about.html b/server/djrandom/frontend/templates/about.html index 5c737152e9022947036f2c600a6e57e729f73682..aedc42e9eca58f0f9c1b254169ec5298eedadc17 100644 --- a/server/djrandom/frontend/templates/about.html +++ b/server/djrandom/frontend/templates/about.html @@ -1,14 +1,7 @@ -<!doctype html> -<html> - <head> - <title>DJ RANDOM :: About</title> - <link href="/static/favicon.ico" type="image/x-icon" rel="shortcut icon"> - <link rel="stylesheet" type="text/css" href="/static/css/login.css"> - </head> - <body> +{% extends "_std_base.html" %} +{% block title %}About{% endblock %} +{% block main %} - <div id="main"> - <div id="form"> <h3>DJ:Random</h3> <p> @@ -19,9 +12,4 @@ (<b>{{ used_gb }}</b> Gb). </p> - </div> - - </div> - - </body> -</html> +{% endblock %} diff --git a/server/djrandom/frontend/templates/redirect.html b/server/djrandom/frontend/templates/redirect.html new file mode 100644 index 0000000000000000000000000000000000000000..a99041f8d0dbd8c4c6da4f7b034d247a94388af9 --- /dev/null +++ b/server/djrandom/frontend/templates/redirect.html @@ -0,0 +1,13 @@ +{% extends "_std_base.html" %} +{% block title %}About{% endblock %} +{% block main %} + +<p> + Redirecting to <a href="{{ url | e }}">{{ url | e }}</a> ... +</p> + +<script type="text/javascript"> + window.location = '{{ url | e }}'; +</script> + +{% endblock %} diff --git a/server/djrandom/frontend/templates/user_details.html b/server/djrandom/frontend/templates/user_details.html index 5afee9535746294fe59a30edbbc477f02703055a..6b0d9432d5a4541ee4d31b314003a8a6a0085040 100644 --- a/server/djrandom/frontend/templates/user_details.html +++ b/server/djrandom/frontend/templates/user_details.html @@ -1,13 +1,7 @@ -<!doctype html> -<html> - <head> - <title>DJ RANDOM</title> - <link href="/static/favicon.ico" type="image/x-icon" rel="shortcut icon"> - <link rel="stylesheet" type="text/css" href="/static/css/login.css"> - </head> - <body> - - <div id="main"> +{% extends "_std_base.html" %} +{% block title %}User Details{% endblock %} +{% block main %} + <div id="form"> {%- for msg in get_flashed_messages() -%} @@ -55,5 +49,4 @@ </div> - </body> -</html> +{% endblock %} diff --git a/server/djrandom/frontend/templates/user_invite.html b/server/djrandom/frontend/templates/user_invite.html index 2cc3afa87234d1a98592291b67108262b56a2a47..ee7625b4e39ef270fb1db98e02efb9abb7da611b 100644 --- a/server/djrandom/frontend/templates/user_invite.html +++ b/server/djrandom/frontend/templates/user_invite.html @@ -1,14 +1,7 @@ -<!doctype html> -<html> - <head> - <title>DJ RANDOM</title> - <link href="/static/favicon.ico" type="image/x-icon" rel="shortcut icon"> - <link rel="stylesheet" type="text/css" href="/static/css/login.css"> - </head> - <body> +{% extends "_std_base.html" %} +{% block title %}Invites{% endblock %} +{% block main %} - <div id="main"> - <div id="form"> <form action="/user/invite" method="post"> {{ form.hidden_tag() | safe }} @@ -27,9 +20,5 @@ <input type="submit" class="f_submit" value=" Invite "> </p> </form> - </div> - </div> - - </body> -</html> +{% endblock %} diff --git a/server/djrandom/frontend/views.py b/server/djrandom/frontend/views.py index 662b70170e82ddf89c282faf4c014075a084dbcc..ec792dd7e8b7defdc867fa880aefc95ea0d5c09c 100644 --- a/server/djrandom/frontend/views.py +++ b/server/djrandom/frontend/views.py @@ -52,6 +52,15 @@ def homepage(): return render_template('index.html', user=user) +@app.route('/ext') +@require_auth +def redirect_to_external_url(): + url = request.args.get('url') + if not url: + abort(400) + return render_template('redirect.html', url=url) + + def fileiter(path, pos, end): with open(path, 'r') as fd: fd.seek(pos) diff --git a/server/djrandom/model/mp3.py b/server/djrandom/model/mp3.py index cb2c220243696195566e1879f2870a6a534d017d..f9314351cc7313dd1b1470c98a74443a85e450cf 100644 --- a/server/djrandom/model/mp3.py +++ b/server/djrandom/model/mp3.py @@ -1,3 +1,4 @@ +import random from sqlalchemy import * from datetime import datetime, timedelta from djrandom.database import Base, Session @@ -42,6 +43,17 @@ class MP3(Base): return cls.query.filter_by(ready=True).order_by( desc(cls.uploaded_at)).limit(n) + @classmethod + def get_random_songs(cls, n=10): + """Return N completely random songs.""" + results = [] + num_songs = cls.query.filter_by(ready=True).count() + for idx in xrange(n): + song = cls.query.filter_by(ready=True).limit(1).offset( + random.randint(0, num_songs - 1)).one() + results.append(song) + return results + class PlayLog(Base):