We are no longer offering accounts on this server. Consider https://gitlab.freedesktop.org/ as a place to host projects.

Commit cc4c1755 authored by Daniel Watkins's avatar Daniel Watkins

Merged djangofm work.

parent 987da5f8
import md5
from django.contrib import admin
from django.contrib.auth.models import get_hexdigest, User
from django.contrib.auth.admin import UserAdmin
from django.template.defaultfilters import slugify
from django.db import models
RATING_CHOICES = (
('L', 'Love'),
('B', 'Ban'),
('S', 'Skip')
)
SOURCE_CHOICES = (
('P', 'Chosen by the user'),
('R', 'Non-personalised broadcast'),
('E', 'Personalised recommendation except Last.fm'),
('L', 'Last.fm'),
('U', 'Unknown')
)
class GobblerUser(User):
def set_password(self, raw_password):
print "set password"
import random
algo = 'sha1'
salt = get_hexdigest(algo, str(random.random()), str(random.random()))[:5]
hsh = get_hexdigest(algo, salt, raw_password)
self.password = '%s$%s$%s' % (algo, salt, hsh)
pwd,created = Md5Password.objects.get_or_create(user=self)
pwd.password = md5.new(raw_password).hexdigest()
pwd.save()
def get_md5(self):
pwd = Md5Password.objects.get(user=self)
return pwd.password
class Meta:
proxy = True
admin.site.register(GobblerUser, UserAdmin)
class Artist(models.Model):
name = models.CharField(max_length=256, unique=True)
slug = models.SlugField(unique=True, editable=False)
def get_absolute_url(self):
return "/music/%s/" % (self.slug,)
def save(self, force_insert=False, force_update=False):
self.slug = slugify(self.name)
super(Artist, self).save(force_insert, force_update)
def __unicode__(self):
return self.name
class Album(models.Model):
name = models.CharField(max_length=256)
artist = models.ForeignKey(Artist)
slug = models.SlugField(editable=False)
def get_absolute_url(self):
return "%s%s/" % (self.artist.get_absolute_url(), self.slug)
def save(self, force_insert=False, force_update=False):
self.slug = slugify(self.name)
super(Album, self).save(force_insert, force_update)
def __unicode__(self):
return "%s by %s" % (self.name, self.artist)
class Meta:
unique_together = (('artist', 'name'), ('artist', 'slug'))
class Track(models.Model):
name = models.CharField(max_length=256)
track_number = models.PositiveSmallIntegerField(blank=True, null=True)
length = models.PositiveSmallIntegerField(blank=True)
album = models.ForeignKey(Album, null=True)
mbid = models.CharField(max_length=256, blank=True)
slug = models.SlugField(unique=True, editable=False)
def get_absolute_url(self):
return "%s%s/" % (self.album.get_absolute_url(), self.name)
def save(self, force_insert=False, force_update=False):
self.slug = slugify(self.name)
super(Track, self).save(force_insert, force_update)
def __unicode__(self):
return "%s - %s" % (self.album.artist, self.name)
class Session(models.Model):
user = models.OneToOneField(GobblerUser, primary_key=True)
key = models.CharField(max_length=32)
class Md5Password(models.Model):
user = models.OneToOneField(GobblerUser, primary_key=True)
password = models.CharField(max_length=32)
class NowPlaying(models.Model):
user = models.OneToOneField(GobblerUser, primary_key=True)
track = models.ForeignKey(Track)
def __unicode__(self):
return "%s is playing %s" % (self.user, self.track)
class Gobble(models.Model):
user = models.ForeignKey(GobblerUser)
track = models.ForeignKey(Track)
time = models.DateTimeField()
source = models.CharField(choices=SOURCE_CHOICES, max_length=1)
rating = models.CharField(choices=RATING_CHOICES, max_length=1,
blank=True)
length = models.PositiveSmallIntegerField()
@property
def artist(self):
return self.track.album.artist
def __unicode__(self):
return "%s at %s" % (self.user, self.time)
admin.site.register(Gobble)
from datetime import datetime
import md5
import random
from django.http import HttpResponse
from gobbler.models import Album, Artist, Gobble, GobblerUser, Md5Password, NowPlaying, Session, Track
from web.views import frontpage
def handshake_11(request):
g = request.GET
try:
client_id,client_version,user = g['c'], g['v'], g['u']
except KeyError:
return HttpResponse("FAILED The request was malformed\nINTERVAL 0\n")
challenge = md5.new(str(random.random())).hexdigest()
try:
guser = GobblerUser.objects.get(username=user)
except GobblerUser.DoesNotExist:
return HttpResponse("BADUSER\nINTERVAL 0\n")
session,created = Session.objects.get_or_create(user=guser)
session.key = challenge
session.save()
return HttpResponse("\n".join(["UPTODATE",
challenge,
"http://127.0.0.1:80/protocol_1.1/",
"INTERVAL 0"]))
def handshake_12(request):
g = request.GET
try:
client_id,client_version,user,timestamp,auth = (
g['c'], g['v'], g['u'], g['t'], g['a'])
except KeyError:
return HttpResponse("FAILED The request was malformed\n")
try:
guser = GobblerUser.objects.get(username=user)
except GobblerUser.DoesNotExist:
return HttpResponse("BADAUTH\n")
expected_token = md5.new(guser.get_md5() + timestamp)
if auth != expected_token.hexdigest():
return HttpResponse("BADAUTH\n")
session_id = expected_token.copy()
session_id.update(str(datetime.now()))
session,created = Session.objects.get_or_create(user=guser)
session.key = session_id.hexdigest()
session.save()
return HttpResponse("\n".join(["OK",
session_id.hexdigest(),
"http://127.0.0.1:80/nowplaying/",
"http://127.0.0.1:80/protocol_1.2/"]))
def index(request):
g = request.GET
handshake = g.get('hs', None)
if handshake:
try:
protocol_version = g['p']
except KeyError:
return HttpResponse("FAILED The request was malformed\n")
if protocol_version == "1.2" or protocol_version == "1.2.1":
return handshake_12(request)
elif protocol_version == "1.1":
return handshake_11(request)
else:
return HttpResponse("FAILED Unsupported protocol version\n")
else:
return frontpage(request)
def now_playing(request):
p = request.POST
session_id,artist_name,track_name,album_name,length,tracknumber,mbid = (
p['s'], p['a'], p['t'], p['b'], p['l'], p['n'], p['m'])
try:
session = Session.objects.get(key=session_id)
except Session.DoesNotExist:
return HttpResponse("BADSESSION\n")
artist,c = Artist.objects.get_or_create(name=artist_name)
album,c = Album.objects.get_or_create(name=album_name,
artist=artist)
track,c = Track.objects.get_or_create(name=track_name,
album=album,
track_number=tracknumber,
length=length,
mbid=mbid)
try:
np = NowPlaying.objects.get(user=session.user)
except NowPlaying.DoesNotExist:
np = NowPlaying()
np.user = session.user
np.track = track
np.save()
return HttpResponse("OK\n")
def _count_submissions(request):
i = 0
while True:
try:
p['a[%d]' % i+1]
i += 1
except:
break
return i
def _get_info(p, j):
artist_name = p['a[%d]' % j]
track_name = p['t[%d]' % j]
album_name = p['b[%d]' % j]
time = p['i[%d]' % j]
mbid = p['m[%d]' % j]
length = p['l[%d]' % j]
return artist_name, track_name, album_name, time, mbid, length
def protocol_11(request):
try:
p = request.POST
try:
guser = GobblerUser.objects.get(username=p['u'])
except GobblerUser.DoesNotExist:
return HttpResponse("BADAUTH\nINTERVAL 0\n")
try:
session = Session.objects.get(user=guser)
except Session.DoesNotExist:
return HttpResponse("BADAUTH\nINTERVAL 0\n")
expected_token = md5.new(guser.get_md5() + session.key)
if p['s'] != expected_token.hexdigest():
return HttpResponse("BADAUTH\nINTERVAL 0\n")
i = _count_submissions(request)
for j in range(i+1):
artist_name,track_name,album_name,time,mbid,length = _get_info(p, j)
artist,c = Artist.objects.get_or_create(name=artist_name)
album,c = Album.objects.get_or_create(name=album_name,
artist=artist)
track,c = Track.objects.get_or_create(name=track_name,
length=length,
album=album,
mbid=mbid)
dt = datetime.strptime(time.replace("\0", ""), "%Y-%m-%d %H:%M:%S")
Gobble.objects.create(user=guser,
track=track,
time=dt,
source="U",
length=length)
return HttpResponse("OK\nINTERVAL 0\n")
except:
import traceback
traceback.print_exc()
raise
def protocol_12(request):
p = request.POST
try:
session = Session.objects.get(key=p['s'])
except Session.DoesNotExist:
return HttpResponse("BADSESSION\n")
i = _count_submissions(request)
for j in range(i+1):
artist_name,track_name,album_name,time,mbid,length = _get_info(p, j)
source = p['o[%d]' % j]
rating = p['r[%d]' % j]
tracknumber = p['n[%d]' % j]
artist,c = Artist.objects.get_or_create(name=artist_name)
album,c = Album.objects.get_or_create(name=album_name,
artist=artist)
track,c = Track.objects.get_or_create(name=track_name,
album=album,
track_number=tracknumber,
length=length,
mbid=mbid)
dt = datetime.fromtimestamp(float(time))
gobble = Gobble.objects.create(user=session.user,
track=track,
time=dt,
source=source,
rating=rating,
length=length)
return HttpResponse('OK\n')
#!/usr/bin/python
from django.core.management import execute_manager
try:
import settings # Assumed to be in the same directory.
except ImportError:
import sys
sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
sys.exit(1)
if __name__ == "__main__":
execute_manager(settings)
# Django settings for djangofm project.
import os
PROJECT_ROOT = os.getcwd()
DEBUG = True
TEMPLATE_DEBUG = DEBUG
ADMINS = (
# ('Your Name', 'your_email@domain.com'),
)
MANAGERS = ADMINS
DATABASE_ENGINE = 'sqlite3' # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
DATABASE_NAME = 'gobble.db' # Or path to database file if using sqlite3.
DATABASE_USER = '' # Not used with sqlite3.
DATABASE_PASSWORD = '' # Not used with sqlite3.
DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3.
DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3.
# Local time zone for this installation. Choices can be found here:
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
# although not all choices may be available on all operating systems.
# If running in a Windows environment this must be set to the same as your
# system time zone.
TIME_ZONE = 'Europe/London'
# Language code for this installation. All choices can be found here:
# http://www.i18nguy.com/unicode/language-identifiers.html
LANGUAGE_CODE = 'en-us'
SITE_ID = 1
# If you set this to False, Django will make some optimizations so as not
# to load the internationalization machinery.
USE_I18N = True
# Absolute path to the directory that holds media.
# Example: "/home/media/media.lawrence.com/"
MEDIA_ROOT = ''
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
# trailing slash if there is a path component (optional in other cases).
# Examples: "http://media.lawrence.com", "http://example.com/media/"
MEDIA_URL = ''
# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
# trailing slash.
# Examples: "http://foo.com/media/", "/media/".
ADMIN_MEDIA_PREFIX = '/media/'
# Make this unique, and don't share it with anybody.
SECRET_KEY = 'w3i7!4=(&varwup7-!g8r)&n%@9*f+pj)^iot0p_dbgh1s9z&u'
# List of callables that know how to import templates from various sources.
TEMPLATE_LOADERS = (
'django.template.loaders.filesystem.load_template_source',
'django.template.loaders.app_directories.load_template_source',
# 'django.template.loaders.eggs.load_template_source',
)
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
)
ROOT_URLCONF = 'djangofm.urls'
TEMPLATE_DIRS = (
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
# Always use forward slashes, even on Windows.
# Don't forget to use absolute paths, not relative paths.
os.path.join(PROJECT_ROOT, 'templates'),
)
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'gobbler',
)
{% extends "base.html" %}
{% block main %}
<h2>{{ album.artist }} - {{ album.name }}</h2>
<ul id="tracks">
{% for track in tracks %}
<li><a href="{{ track.get_absolute_url }}">{{ track.name }}</a></li>
{% endfor %}
</ul>
{% endblock %}
{% extends "base.html" %}
{% block main %}
<h2>{{ artist }}</h2>
<ul id="albums" class="listcloud">
{% for album in albums %}
<li>
<dl>
<dt><a href="{{ album.get_absolute_url }}">{{ album.name }}</a></dt>
<dd></dd>
</dl>
</li>
{% endfor %}
</ul>
{% endblock %}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>django.fm</title>
<link rel="stylesheet" href="http://alpha.libre.fm/themes/librefm/base.css" type="text/css">
<link rel="stylesheet" href="http://alpha.libre.fm/themes/librefm/librefm.css" type="text/css">
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
</head>
<body>
<div id="doc3" class="yui-t7">
<div id="hd" class='vcard'><h1 class='fn org'><a href="/" class='url'>django.fm</a></h1></div>
<div id="menu">
<ul id="navigation">
{% if user.is_authenticated %}
<li><a href="/profile/{{ user.username }}">{{ user }}</a></li>
<li>Invites disabled</li>
{% else %}
<li><a href="/login/">Login</a></li>
<li><a href="/request/">Request invitation</a></li>
{% endif %}
<li>Explore:
<li><a href="/explore/artists/">Artists</a></li>
</li>
</ul>
</div>
<div id="main">
{% block main %}
{% endblock %}
<div class="cleaner">&nbsp;</div>
</div>
</div>
<div id='footer'>
<p><a rel='license' href='http://creativecommons.org/licenses/by-sa/3.0/us/'><img src='http://alpha.libre.fm/themes/librefm/images/cc-by-sa.png'/></a></p>
</div>
</body>
</html>
{% extends "base.html" %}
{% block main %}
<h2>Explore popular artists</h2>
<ul id="topartists" class="listcloud">
{% for artist in artists %}
<li>
<dl>
<dt><a href="{{ artist.artist.get_absolute_url }}">{{ artist.artist }}</a></dt>
<dd>{{ artist.count }} gobbles</dd>
</dl>
</li>
{% endfor %}
</ul>
{% endblock %}
{% extends "base.html" %}
{% block main %}
<h2>Welcome</h2>
<p><strong><span class='vcard fn org'>django.fm</span></strong> is a free
network service that will allow users to share their musical tastes with other
people.</p>
<h3>What's hot? Recently gobbled.</h3>
<ul id="recenttracks" class="listcloud">
{% for gobble in recently_gobbled %}
<li>
<dl>
<dt><a href="{{ gobble.artist.get_absolute_url }}">{{ gobble.artist }}</a></dt>
<dd>{{ gobble.track.name }}</dd>
</dl>
</li>
{% endfor %}
</ul>
{% endblock %}
{% extends "base.html" %}
{% block main %}
<h2>{{ user }}'s profile</h2>
<h3>Latest 20 Gobbles</h3>
{% regroup gobbles by artist as gobble_list %}
{% for artist in gobble_list %}
<dl class="gobbles">
<dt class="artist"><a href="{{ artist.grouper.get_absolute_url }}">{{ artist.grouper }}</a></dt>
{% for gobble in artist.list %}
<dd class="gobble"><span class='track-name'>{{ gobble.track.name }}</span></dd>
{% endfor %}
</dl>
{% endfor %}
{% endblock %}
{% extends "base.html" %}
{% block main %}
<h2>Login</h2>
<div id="login">
<form method="post" action="">
{{ form.as_p }}
<input type="submit" value="Let me in!" />
<input type="hidden" name="next" value="/" />
</form>
</div>
{% endblock %}
from django.conf.urls.defaults import *
from django.contrib import admin
from gobbler.views import index, now_playing, protocol_11, protocol_12
from web.views import album, artist, explore_artists, profile
admin.autodiscover()
urlpatterns = patterns('',
(r'^admin/(.*)', admin.site.root),
(r'^$', index),
(r'^protocol_1.1/$', protocol_11),
(r'^protocol_1.2/$', protocol_12),
(r'^nowplaying/$', now_playing),
(r'^explore/artists/$', explore_artists),
(r'^music/(.*)/(.*)/$', album),
(r'^music/(.*)/$', artist),
(r'^login/$', 'django.contrib.auth.views.login'),
(r'^profile/(.*)/$', profile),
)
from django.db import models
# Create your models here.
"""
This file demonstrates two different styles of tests (one doctest and one
unittest). These will both pass when you run "manage.py test".
Replace these with more appropriate tests for your application.
"""
from django.test import TestCase
class SimpleTest(TestCase):
def test_basic_addition(self):
"""
Tests that 1 + 1 always equals 2.
"""
self.failUnlessEqual(1 + 1, 2)
__test__ = {"doctest": """
Another way to test that 1 + 1 is equal to 2.
>>> 1 + 1 == 2
True
"""}
from django.db.models import Count
from django.shortcuts import get_object_or_404, render_to_response
from django.template import RequestContext
from gobbler.models import Album, Artist, Gobble, GobblerUser
def album(request, artist_slug, album_slug):
artist = get_object_or_404(Artist, slug=artist_slug)
album = get_object_or_404(Album, artist=artist, slug=album_slug)
tracks = album.track_set.order_by('track_number')
return render_to_response("album.html", {'album': album, 'tracks': tracks},
context_instance=RequestContext(request))