Skip to content
Snippets Groups Projects
Commit b9be3109 authored by ale's avatar ale
Browse files

add a playlist model and API; add USERID cookie for user recognition; autosave playlists

parent d15ea5ba
No related branches found
No related tags found
No related merge requests found
...@@ -18,7 +18,7 @@ class SetTextFactory(PoolListener): ...@@ -18,7 +18,7 @@ class SetTextFactory(PoolListener):
def init_db(uri): def init_db(uri):
from djrandom.model import mp3 from djrandom.model import mp3, playlist
engine = create_engine(uri, listeners=[SetTextFactory()]) engine = create_engine(uri, listeners=[SetTextFactory()])
Session.configure(bind=engine) Session.configure(bind=engine)
Base.metadata.create_all(engine) Base.metadata.create_all(engine)
...@@ -2,7 +2,8 @@ import sys ...@@ -2,7 +2,8 @@ import sys
import os import os
import optparse import optparse
import logging import logging
from flask import Flask, request, Response, abort, jsonify, render_template from flask import Flask, request, Response, abort, jsonify, \
render_template, session
from djrandom import daemonize from djrandom import daemonize
from djrandom import utils from djrandom import utils
from djrandom.model.mp3 import MP3 from djrandom.model.mp3 import MP3
...@@ -13,9 +14,38 @@ from sqlalchemy import distinct ...@@ -13,9 +14,38 @@ from sqlalchemy import distinct
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
app = Flask(__name__) app = Flask(__name__)
app.secret_key = 'J@9als[13- "!>0@!!zWz}='
storage_root = None storage_root = None
searcher = None searcher = None
USERID_COOKIE = 'USERID'
@app.before_request
def check_session_before_request():
# Recover the user id from the long-term cookie, if present,
# otherwise just generate a new one.
if 'userid' not in session:
if USERID_COOKIE in request.cookies:
session['userid'] = request.cookies[USERID_COOKIE]
else:
session['userid'] = utils.random_token()
session['_please_save_userid'] = True
@app.after_request
def save_session_after_request(resp):
if session.get('_please_save_userid'):
del session['_please_save_userid']
resp.set_cookie(USERID_COOKIE, value=session['userid'],
expires=(2<<30) - 1)
return resp
@app.teardown_request
def shutdown_dbsession(exception=None):
Session.remove()
@app.route('/json/artists') @app.route('/json/artists')
def all_artists_json(): def all_artists_json():
...@@ -59,6 +89,30 @@ def play_callback(sha1): ...@@ -59,6 +89,30 @@ def play_callback(sha1):
return jsonify(status=True) return jsonify(status=True)
@app.route('/json/playlist/save', methods=['POST'])
def save_playlist():
hashes = request.form.get('h', '')
uuid = request.form.get('uuid')
if uuid:
playlist = Playlist.query.get(uuid)
if not playlist:
abort(404)
else:
playlist = Playlist(uuid=uuid, userid=session['userid'])
playlist.modified_at = datetime.now()
Session.add(playlist)
Session.commit()
return jsonify(uuid=uuid, status=True)
@app.route('/json/playlist/get/<uuid>')
def playlist_info_json(uuid):
playlist = Playlist.query.get(uuid)
if not playlist:
abort(404)
return jsonify(playlist.to_dict())
@app.route('/json/search') @app.route('/json/search')
def search_json(): def search_json():
query = request.args.get('q') query = request.args.get('q')
...@@ -92,7 +146,7 @@ def songs_fragment(): ...@@ -92,7 +146,7 @@ def songs_fragment():
@app.route('/') @app.route('/')
def homepage(): def homepage():
return render_template('index.html') return render_template('index.html', userid=session['userid'])
def fileiter(path, pos, end): def fileiter(path, pos, end):
......
...@@ -3,7 +3,9 @@ ...@@ -3,7 +3,9 @@
djr = {}; djr = {};
// Global state. // Global state.
djr.state = {}; djr.state = {
userid: null
};
// Debugging. // Debugging.
djr.debug = function(msg) { djr.debug = function(msg) {
...@@ -92,8 +94,10 @@ djr.doSearch = function() { ...@@ -92,8 +94,10 @@ djr.doSearch = function() {
hashes.push(item.sha1); hashes.push(item.sha1);
} }
}); });
// Clear the current playlist and save a new one.
djr.state.player.clearPlaylist();
djr.state.player.setPlaylist(hashes); djr.state.player.setPlaylist(hashes);
djr.debug('new playlist: ' + hashes.join(', ')); djr.state.player.savePlaylist();
// Load the HTML rendering of the playlist. // Load the HTML rendering of the playlist.
djr.loadPlaylistHtml(); djr.loadPlaylistHtml();
...@@ -102,7 +106,9 @@ djr.doSearch = function() { ...@@ -102,7 +106,9 @@ djr.doSearch = function() {
// Initialization. // Initialization.
djr.init = function() { djr.init = function(userid) {
djr.state.userid = userid;
// Restore state if the URL changes. // Restore state if the URL changes.
$(window).bind('hashchange', djr.history.restore); $(window).bind('hashchange', djr.history.restore);
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
djr.Player = function(selector) { djr.Player = function(selector) {
this.player = $(selector); this.player = $(selector);
this.curPlaylist = Array(); this.curPlaylist = Array();
this.curPlaylistId = null;
this.curIdx = -1; this.curIdx = -1;
this.curSong = null; this.curSong = null;
...@@ -28,15 +29,45 @@ djr.Player.prototype.removeFromPlaylist = function(sha1) { ...@@ -28,15 +29,45 @@ djr.Player.prototype.removeFromPlaylist = function(sha1) {
// Empty the current playlist. // Empty the current playlist.
djr.Player.prototype.clearPlaylist = function() { djr.Player.prototype.clearPlaylist = function() {
this.curPlaylist = Array(); this.curPlaylist = Array();
this.curPlaylistId = null;
this.curIdx = -1; this.curIdx = -1;
}; };
// Set the playlist to a new set of songs. // Set the playlist to a new set of songs.
djr.Player.prototype.setPlaylist = function(hashes) { djr.Player.prototype.setPlaylist = function(hashes) {
djr.debug('new playlist: ' + hashes.join(', '));
this.curPlaylist = hashes; this.curPlaylist = hashes;
this.curIdx = -1; this.curIdx = -1;
}; };
// Save the current playlist.
djr.Player.prototype.savePlaylist = function() {
$.ajax({url: '/json/playlist/save',
data: {uuid: this.curPlaylistId,
h: this.curPlaylist.join(',')},
dataType: 'json',
type: 'POST',
context: this,
success: function(data, status, jqxhr) {
if (data.status && data.uuid != this.curPlaylistId) {
this.curPlaylistId = data.uuid;
djr.debug('created new playlist, UUID=' + data.uuid);
}
}
});
};
// Load a playlist.
djr.Player.prototype.loadPlaylist = function(uuid) {
$.ajax({url: '/json/playlist/get/' + uuid,
dataType: 'json',
context: this,
success: function(data, status, jqxhr) {
this.setPlaylist(data.songs);
}
});
};
// Start playing the next song in the playlist. // Start playing the next song in the playlist.
djr.Player.prototype.nextSong = function() { djr.Player.prototype.nextSong = function() {
var next_idx = this.curIdx + 1; var next_idx = this.curIdx + 1;
......
...@@ -29,7 +29,9 @@ $(document).ready(function() { ...@@ -29,7 +29,9 @@ $(document).ready(function() {
}); });
$('#queryField').focus(); $('#queryField').focus();
$('#searchForm').submit(do_search); $('#searchForm').submit(do_search);
djr.init();
// Initialize DJR with the current userid.
djr.init('{{ userid }}');
}); });
</script> </script>
{% endblock %} {% endblock %}
......
...@@ -21,7 +21,7 @@ class MP3(Base): ...@@ -21,7 +21,7 @@ class MP3(Base):
album = Column(Unicode(256)) album = Column(Unicode(256))
genre = Column(Unicode(64)) genre = Column(Unicode(64))
uploaded_at = Column(DateTime()) uploaded_at = Column(DateTime())
play_count = Column(Integer(default=0)) play_count = Column(Integer(), default=0)
IDX_SHA1 = 0 IDX_SHA1 = 0
IDX_PATH = 1 IDX_PATH = 1
......
import os
import hashlib
from sqlalchemy import *
from djrandom import utils
from djrandom.database import Base
class Playlist(Base):
__tablename__ = 'playlists'
uuid = Column(String(40), primary_key=True)
userid = Column(String(40), index=True)
modified_at = Column(DateTime())
play_count = Column(Integer(), default=0)
contents = Column(Text())
def __init__(self, **kw):
if kw and 'uuid' not in kw:
kw['uuid'] = utils.random_token()
for k, v in kw.items():
setattr(self, k, v)
def to_dict(self):
return {'uuid': self.uuid,
'songs': self.contents.split(',')}
import hashlib import hashlib
import os import os
NESTING = 2 PATH_NESTING = 2
def generate_path(base_dir, sha1): def generate_path(base_dir, sha1):
dir_parts = [base_dir] dir_parts = [base_dir]
dir_parts.extend(sha1[:NESTING]) dir_parts.extend(sha1[:PATH_NESTING])
base_path = os.path.join(*dir_parts) base_path = os.path.join(*dir_parts)
if not os.path.isdir(base_path): if not os.path.isdir(base_path):
os.makedirs(base_path) os.makedirs(base_path)
return os.path.join(base_path, sha1) return os.path.join(base_path, sha1)
def random_token():
return hashlib.sha1(os.urandom(20)).hexdigest()
def sha1_of_file(path): def sha1_of_file(path):
with open(path, 'r') as fd: with open(path, 'r') as fd:
sha = hashlib.sha1() sha = hashlib.sha1()
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment