Showing preview only (270K chars total). Download the full file or copy to clipboard to get everything.
Repository: lociii/jukebox
Branch: master
Commit: eb9d1795f022
Files: 63
Total size: 252.6 KB
Directory structure:
gitextract_yt7p7x97/
├── .gitignore
├── CHANGES.txt
├── LICENSE.rst
├── MANIFEST.in
├── README.rst
├── bin/
│ └── jukebox
├── jukebox/
│ ├── __init__.py
│ ├── jukebox.wsgi
│ ├── jukebox_core/
│ │ ├── __init__.py
│ │ ├── admin.py
│ │ ├── api.py
│ │ ├── docs/
│ │ │ └── API.rst
│ │ ├── forms.py
│ │ ├── management/
│ │ │ ├── __init__.py
│ │ │ └── commands/
│ │ │ ├── __init__.py
│ │ │ ├── jukebox_index.py
│ │ │ └── jukebox_setup.py
│ │ ├── migrations/
│ │ │ ├── 0001_initial.py
│ │ │ ├── 0002_auto__del_field_album_Artist.py
│ │ │ └── __init__.py
│ │ ├── models.py
│ │ ├── tests/
│ │ │ ├── __init__.py
│ │ │ ├── api.py
│ │ │ ├── api_albums.py
│ │ │ ├── api_artists.py
│ │ │ ├── api_favourites.py
│ │ │ ├── api_genres.py
│ │ │ ├── api_history.py
│ │ │ ├── api_queue.py
│ │ │ ├── api_songs.py
│ │ │ └── api_years.py
│ │ ├── urls.py
│ │ ├── utils.py
│ │ └── views.py
│ ├── jukebox_web/
│ │ ├── __init__.py
│ │ ├── locale/
│ │ │ ├── de/
│ │ │ │ └── LC_MESSAGES/
│ │ │ │ ├── django.mo
│ │ │ │ ├── django.po
│ │ │ │ ├── djangojs.mo
│ │ │ │ └── djangojs.po
│ │ │ ├── en/
│ │ │ │ └── LC_MESSAGES/
│ │ │ │ ├── django.mo
│ │ │ │ ├── django.po
│ │ │ │ ├── djangojs.mo
│ │ │ │ └── djangojs.po
│ │ │ ├── fr/
│ │ │ │ └── LC_MESSAGES/
│ │ │ │ ├── django.mo
│ │ │ │ ├── django.po
│ │ │ │ ├── djangojs.mo
│ │ │ │ └── djangojs.po
│ │ │ └── pt_BR/
│ │ │ └── LC_MESSAGES/
│ │ │ ├── django.mo
│ │ │ ├── django.po
│ │ │ ├── djangojs.mo
│ │ │ └── djangojs.po
│ │ ├── static/
│ │ │ ├── css/
│ │ │ │ └── music.css
│ │ │ └── js/
│ │ │ └── music.js
│ │ ├── templates/
│ │ │ ├── index.html
│ │ │ └── login.html
│ │ ├── urls.py
│ │ └── views.py
│ ├── manage.py
│ ├── settings.py
│ ├── settings_local.example.py
│ └── urls.py
├── requirements.txt
└── setup.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
.idea/
dist/
jukebox.egg-info/
*.pyc
*.sqlite
*.pid
jukebox/settings_local.py
build/
*.sw*
================================================
FILE: CHANGES.txt
================================================
v0.1.0, 2011-11-17 -- Initial release
v0.1.1, 2011-11-21 -- Fixed installer bugs, added personal history, added system tests for api
v0.2.0, 2011-11-24 -- Language switch, sortable lists, google-like search operators, autoplay tries to play appropriate music, improved web interface
v0.2.1, 2011-11-24 -- fixed issue with autoplay
v0.3.0, 2011-11-28 -- Added jukebox_watch, added list of voters, minor improvements
v0.3.1, 2012-09-11 -- Improved exception handling, added rss for current song, minor bug fixes
v0.3.2, 2013-02-24 -- Update dependencies, fix authentication problems, switch from inotify to watchdog
v0.3.3, 2013-02-28 -- Fix manifest
v0.3.4, 2013-04-12 -- Fix to skip unauthorized sessions, updated wsgi handler
v0.3.5, 2013-05-17 -- Update mutagen, fixed minor bugs
v0.3.7, 2013-05-17 -- Fix buggy pypi package
v0.4.0, 2013-06-25 -- Split jukebox in different packages, Strip artist from album data
v0.4.1, 2013-06-25 -- Add missing wsgi file
================================================
FILE: LICENSE.rst
================================================
The MIT License
Copyright (c) Jens Nistler, http://jensnistler.de
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
================================================
FILE: MANIFEST.in
================================================
include CHANGES.txt LICENSE.rst README.rst
recursive-include jukebox/jukebox_core/docs *
recursive-include jukebox/jukebox_web/locale *
recursive-include jukebox/jukebox_web/static *
recursive-include jukebox/jukebox_web/templates *
include jukebox/jukebox.wsgi
================================================
FILE: README.rst
================================================
Unmaintained
============
The big time of local mp3 libraries is over. Therefore this project has been abandoned and is currently unmaintained.
Maybe have a look at https://qca.st/ which provides jukebox functionalities for music streaming services.
Democratic Jukebox - your democratic music player
==================================================
Ever wanted to listen to music with a larger group of people e.g. in your office? Who decides what to play?
Make your music player democratic and give everyone the chance to promote their favourite song.
Jukebox provides a web interface to search your music library and vote for songs to be played.
The more votes a song gets, the sooner you will listen to it.
At one point in your life your play queue might get empty. Don't worry, the jukebox will keep on playing.
The playback system figures out who is online using the web interface or API and plays music to their liking.
**Required system libraries**
libshout3, libshout3-dev and python-dev are required to build the dependecy `python-shout <http://pypi.python.org/pypi/python-shout>`_.
.. image:: http://static.jensnistler.de/jukebox.png
:height: 404px
:width: 872px
:scale: 100%
:alt: Democratic Jukebox - your democratic music player
General
========
- Jukebox is available in english, german and brazilian portuguese
- Jukebox uses Facebook, Twitter and Github for authentication (see `django-social-auth <https://github.com/omab/django-social-auth>`_ for more authentication providers)
Setup
==================
Install `virtualenvwrapper <https://pypi.python.org/pypi/virtualenvwrapper>`_ via `pip <http://pypi.python.org/pypi/pip>`_ if not alreay done:
::
sudo pip install virtualenvwrapper
Set up a project for jukebox:
::
mkproject jukebox
Install the jukebox in your fresh virtual environment:
::
workon jukebox
pip install jukebox
Now it's time to configure the jukebox
1. Enter admin credentials and select authentication providers
2. Create the database
3. Index your music
That's all
::
jukebox jukebox_setup
jukebox syncdb
jukebox migrate
jukebox jukebox_index --path=/path/to/library
The django builtin development webserver will be sufficient to serve your office or party. Just start it up:
::
jukebox runserver ip:port
Now you're ready to put music in the queue.
Playback
=========
Currently there are two methods of playing the music chosen in jukebox.
**shoutcast**
Stream your music to a shoutcast compatible server
::
pip install jukebox-shout
See `jukebox_shout <https://github.com/lociii/jukebox_shout>`_ for details and startup command.
**mpg123**
Play your music locally on the machine running the jukebox.
::
pip install jukebox-mpg123
See `jukebox_mpg123 <https://github.com/lociii/jukebox_mpg123>`_ for details and startup command.
**Contribute!**
Feel free to write additional playback modules and I'll add them to the list above.
Live indexing
===============
There is no need to update your index every time a new song is added to your library, just use the live indexer package.
::
pip install jukebox-live-indexer
See `jukebox_live_indexer <https://github.com/lociii/jukebox_live_indexer>`_ for details and startup command.
API
=============
jukebox_core provides a fully fledged REST API for authenticated users. See `API reference <https://github.com/lociii/jukebox/blob/master/jukebox/jukebox_core/docs/API.rst>`_
Search filters
===============
Jukebox supports google-like search filter. Available search fields: title, artist, album, genre, year.
::
title:(love to dance) artist:bobby
artist:(bobby baby) lucky
title:(in ten years) genre:electronic
License
========
MIT License. See `License <https://github.com/lociii/jukebox/blob/master/LICENSE.rst>`_
Contribute!
============
You want to contribute to this project? Just fork the repo and do this:
::
mkproject jukebox
git clone git@github.com:[username]/jukebox.git .
git remote add upstream git://github.com/lociii/jukebox.git
pip install -r requirements.txt
cd jukebox
Follow up configuring jukebox like described in Setup. Use ./manage.py instead of the jukebox command.
You can now create a branch to make your actual changes and send a pull request. See `this article <https://www.openshift.com/wiki/github-workflow-for-submitting-pull-requests>`_ for how to do this.
Contributors
=============
- Brazilian portuguese translation by `Luan Fonseca de Farias <https://github.com/luanfonceca>`_
- Bugfixes by `Peter Hoffmann <https://github.com/hoffmann>`_
- Bugfixes by `Amir H. Hajizamani <https://github.com/amirhhz>`_
- Bugfixes by `Gabriel Duman <https://github.com/gabber7>`_
- Bugfixes by `Steffen Zieger <https://github.com/saz>`_
- Bugfixes by `Jonas Baumann <https://github.com/jone>`_
- Bugfixes by `imithun <https://github.com/imithun>`_
Release Notes
==============
0.1.0
- Initial release
0.1.1
- Fixed installer bugs
- Added personal history
- Added system tests for api
0.2.0
- Language switch
- Sortable lists
- Google-like search operators
- Autoplay tries to play appropriate music
- Improved web interface
0.2.1
- fixed issue with autoplay
0.3.0
- Added jukebox_watch
- Added list of voters
- Minor improvements
0.3.1
- Improved exception handling
- Added rss for current song
- Minor bug fixes
0.3.2
- Update dependencies
- Fix authentication problems
- Switch from inotify to watchdog
0.3.3
- Fix manifest
0.3.4
- Fix to skip unauthorized sessions
- Updated wsgi handler
0.3.5
- Update mutagen (Thanks guys for removing old packages)
- Fixed minor bugs (Thanks to `saz <https://github.com/saz/>`_)
0.3.7
- Fix buggy pypi package
0.4.0
- Split jukebox in different packages
- Strip artist from album data
0.4.1
- Add missing wsgi file
================================================
FILE: bin/jukebox
================================================
#!/usr/bin/env python
import imp
import sys
try:
path = imp.find_module('jukebox')[1]
sys.path.append(path)
except ImportError, e:
sys.stderr.write("Can't find jukebox: " + str(e.message) + "\n")
sys.exit(1)
from django.core.management import execute_manager
import settings
if __name__ == '__main__':
execute_manager(settings)
================================================
FILE: jukebox/__init__.py
================================================
================================================
FILE: jukebox/jukebox.wsgi
================================================
import os
import sys
path = os.path.normpath(os.path.dirname(__file__))
if path not in sys.path:
sys.path.append(path)
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'jukebox.settings')
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()
================================================
FILE: jukebox/jukebox_core/__init__.py
================================================
================================================
FILE: jukebox/jukebox_core/admin.py
================================================
# -*- coding: UTF-8 -*-
from models import Artist, Genre, Album, Song, Queue, History, Favourite
from django.contrib import admin
class ArtistAdmin(admin.ModelAdmin):
list_display = ('Name', )
search_fields = ['Name']
class GenreAdmin(admin.ModelAdmin):
list_display = ('Name', )
class AlbumAdmin(admin.ModelAdmin):
list_display = ('Title', )
search_fields = ['Title']
class SongAdmin(admin.ModelAdmin):
list_display = ('Title', 'Artist', 'Year', 'Genre', )
search_fields = ['Title']
class QueueAdmin(admin.ModelAdmin):
list_display = ('Song', 'Created', )
class HistoryAdmin(admin.ModelAdmin):
list_display = ('Song', 'Created', )
class FavouriteAdmin(admin.ModelAdmin):
list_display = ('Song', 'User', 'Created', )
search_fields = ['User__username']
admin.site.register(Artist, ArtistAdmin)
admin.site.register(Genre, GenreAdmin)
admin.site.register(Album, AlbumAdmin)
admin.site.register(Song, SongAdmin)
admin.site.register(Queue, QueueAdmin)
admin.site.register(History, HistoryAdmin)
admin.site.register(Favourite, FavouriteAdmin)
================================================
FILE: jukebox/jukebox_core/api.py
================================================
# -*- coding: UTF-8 -*-
from django.core.paginator import Paginator, InvalidPage
from django.core.exceptions import ObjectDoesNotExist
from django.db import transaction
from django.db.models import Count, Min, Q
from django.contrib.sessions.models import Session
from django.utils import formats
import os, re, time
from datetime import datetime
from signal import SIGABRT
from django.contrib.auth.models import User
from models import Song, Artist, Album, Genre, Queue, Favourite, History, Player
class api_base:
count = 30
user_id = None
search_term = None
search_title = None
search_artist_name = None
search_album_title = None
filter_year = None
filter_genre = None
filter_album_id = None
filter_artist_id = None
order_by_field = None
order_by_direction = None
order_by_fields = []
order_by_directions = ["asc", "desc"]
order_by_default = None
def set_count(self, count):
if count > 100:
self.count = 100
elif count > 0:
self.count = count
def set_user_id(self, user_id):
self.user_id = user_id
def set_search_term(self, term):
options = self.parseSearchString(
(
"title",
"artist",
"album",
"genre",
"year",
),
term
)
for key, value in options.items():
if key == "title":
self.set_search_title(value)
elif key == "artist":
self.set_search_artist_name(value)
elif key == "album":
self.set_search_album_title(value)
elif key == "genre":
try:
genre = Genre.objects.all().filter(Name__iexact=value)[0:1].get()
self.set_filter_genre(genre.id)
except ObjectDoesNotExist:
pass
elif key == "year":
self.set_filter_year(value)
self.search_term = options["term"]
def parseSearchString(self, keywords, term):
values = {}
for i in range(len(keywords)):
do_continue = False
keyword = keywords[i]
value = None
pos = term.find(keyword + ":")
if pos != -1:
value_start = pos + len(keyword) + 1
# no brackets, search for next whitespace
if term[value_start:value_start + 1] != "(":
value_end = term.find(" ", value_start)
if value_end == -1:
value_end = len(term)
do_continue = True
value = term[value_start:value_end]
# search for next closing bracket but count opened ones
else:
i = value_start + 1
bracket_count = 1
while i < len(term):
char = term[i:i+1]
if char == "(":
bracket_count+= 1
elif char == ")":
bracket_count-= 1
if not bracket_count:
value = term[value_start:i+1]
continue
i+= 1
if not value:
value = term[value_start:len(term)]
do_continue = True
if value is not None:
values[keyword] = value
if do_continue:
continue
for key, value in values.items():
term = term.replace(key + ":" + value, "").strip()
if value.startswith("("):
values[key] = value[1:len(value)-1]
values["term"] = re.sub("\s+", " ", term)
return values
def set_search_title(self, term):
self.search_title = term
def set_search_artist_name(self, term):
self.search_artist_name = term
def set_search_album_title(self, term):
self.search_album_title = term
def set_filter_year(self, term):
self.filter_year = term
def set_filter_genre(self, term):
self.filter_genre = term
def set_filter_album_id(self, term):
self.filter_album_id = term
def set_filter_artist_id(self, term):
self.filter_artist_id = term
def set_order_by(self, field, direction="asc"):
if (not field in self.order_by_fields or
not direction in self.order_by_directions):
return
self.order_by_field = field
self.order_by_direction = direction
def get_default_result(self, result_type, page):
search = {}
if self.search_title is not None:
value = self.search_title
if value.find(" ") != -1:
value = "(" + value + ")"
search["title"] = value
if self.search_artist_name is not None:
value = self.search_artist_name
if value.find(" ") != -1:
value = "(" + value + ")"
search["artist"] = value
if self.search_album_title is not None:
value = self.search_album_title
if value.find(" ") != -1:
value = "(" + value + ")"
search["album"] = value
if self.filter_genre is not None:
genre = Genre.objects.all().filter(id=self.filter_genre)[0:1].get()
value = genre.Name
if value.find(" ") != -1:
value = "(" + value + ")"
search["genre"] = value
search["genre_id"] = genre.id
if self.filter_year is not None:
search["year"] = str(self.filter_year)
if self.search_term is not None:
search["term"] = self.search_term
return {
"type": result_type,
"page": page,
"hasNextPage": False,
"itemList": [],
"order": [],
"search": search,
}
def result_add_queue_and_favourite(self, song, dataset):
if not self.user_id is None:
try:
queue = Queue.objects.get(Song=song)
for user in queue.User.all():
if user.id == self.user_id:
dataset["queued"] = True
break
except ObjectDoesNotExist:
pass
try:
user = User.objects.get(id=self.user_id)
Favourite.objects.get(Song=song, User=user)
dataset["favourite"] = True
except ObjectDoesNotExist:
pass
return dataset
def source_set_order(self, object_list):
if not self.order_by_field is None:
field_name = self.order_by_fields.get(self.order_by_field)
if self.order_by_direction == "desc":
field_name = "-" + field_name
return object_list.order_by(field_name)
elif not self.order_by_default is None:
order = []
for key, value in self.order_by_default.items():
order.append(value)
object_list = object_list.order_by(*order)
return object_list
def result_set_order(self, result):
result["order"] = []
if not self.order_by_field is None:
result["order"].append({
"field": self.order_by_field,
"direction": self.order_by_direction,
})
elif not self.order_by_default is None:
for field, order in self.order_by_default.items():
result["order"].append({
"field": field,
"direction": "desc" if order.startswith("-") else "asc",
})
return result
class songs(api_base):
order_by_fields = {
"title": "Title",
"artist": "Artist__Name",
"album": "Album__Title",
"year": "Year",
"genre": "Genre__Name",
"length": "Length",
}
order_by_default = {
"title": "Title",
}
def index(self, page=1):
object_list = Song.objects.all()
# searches
if not self.search_term is None:
object_list = object_list.filter(
Q(Title__contains=self.search_term)
|
Q(Artist__Name__contains=self.search_term)
|
Q(Album__Title__contains=self.search_term)
)
if not self.search_title is None:
object_list = object_list.filter(
Title__contains=self.search_title
)
if not self.search_artist_name is None:
object_list = object_list.filter(
Artist__Name__contains=self.search_artist_name
)
if not self.search_album_title is None:
object_list = object_list.filter(
Album__Title__contains=self.search_album_title
)
# filters
if not self.filter_year is None:
object_list = object_list.filter(
Year__exact=self.filter_year
)
if not self.filter_genre is None:
object_list = object_list.filter(
Genre__exact=self.filter_genre
)
if not self.filter_album_id is None:
object_list = object_list.filter(
Album__exact=self.filter_album_id
)
if not self.filter_artist_id is None:
object_list = object_list.filter(
Artist__exact=self.filter_artist_id
)
# order
object_list = self.source_set_order(object_list)
# prepare result
result = self.get_default_result("songs", page)
# get data
paginator = Paginator(object_list, self.count)
try:
page_obj = paginator.page(page)
except InvalidPage:
return result
result = self.result_set_order(result)
result["hasNextPage"] = page_obj.has_next()
for item in page_obj.object_list:
dataset = {
"id": item.id,
"title": None,
"artist": {
"id": None,
"name": None,
},
"album": {
"id": None,
"title": None,
},
"year": None,
"genre": {
"id": None,
"name": None,
},
"length": None,
"queued": False,
"favourite": False,
}
if not item.Title is None:
dataset["title"] = item.Title
if not item.Artist is None:
dataset["artist"]["id"] = item.Artist.id
dataset["artist"]["name"] = item.Artist.Name
if not item.Album is None:
dataset["album"]["id"] = item.Album.id
dataset["album"]["title"] = item.Album.Title
if not item.Year is None:
dataset["year"] = item.Year
if not item.Genre is None:
dataset["genre"]["id"] = item.Genre.id
dataset["genre"]["name"] = item.Genre.Name
if not item.Length is None:
dataset["length"] = item.Length
dataset = self.result_add_queue_and_favourite(item, dataset)
result["itemList"].append(dataset)
return result
def getNextSong(self):
# commit transaction to force fresh queryset result
try:
transaction.enter_transaction_management()
transaction.commit()
except BaseException:
pass
try:
data = Queue.objects.all()
data = data.annotate(VoteCount=Count("User"))
data = data.annotate(MinCreated=Min("Created"))
data = data.order_by("-VoteCount", "MinCreated")[0:1].get()
self.addToHistory(data.Song, data.User)
song_instance = data.Song
data.delete()
except ObjectDoesNotExist:
try:
song_instance = self.getRandomSongByPreferences()
self.addToHistory(song_instance, None)
except ObjectDoesNotExist:
song_instance = Song.objects.order_by('?')[0:1].get()
self.addToHistory(song_instance, None)
# remove missing files
if not os.path.exists(song_instance.Filename.encode('utf8')):
Song.objects.all().filter(id=song_instance.id).delete()
return self.getNextSong()
return song_instance
def getRandomSongByPreferences(self):
artists = {}
# get logged in users
sessions = Session.objects.exclude(
expire_date__lt=datetime.today()
)
for session in sessions.all():
data = session.get_decoded()
if not "_auth_user_id" in data:
continue
user_id = data["_auth_user_id"]
# get newest favourites
favourites = Favourite.objects.filter(User__id=user_id)[0:30]
for favourite in favourites:
if not favourite.Song.Artist.id in artists:
artists[favourite.Song.Artist.id] = 0
artists[favourite.Song.Artist.id]+= 1
# get last voted songs
votes = History.objects.filter(User__id=user_id)[0:30]
for vote in votes:
if not vote.Song.Artist.id in artists:
artists[vote.Song.Artist.id] = 0
artists[vote.Song.Artist.id]+= 1
# nothing played and no favourites
if not len(artists):
raise ObjectDoesNotExist
# calculate top artists
from operator import itemgetter
sorted_artists = sorted(
artists.iteritems(),
key=itemgetter(1),
reverse=True
)[0:30]
artists = []
for key in range(len(sorted_artists)):
artists.append(sorted_artists[key][0])
# get the 50 last played songs
history = History.objects.all()[0:50]
last_played = []
for item in history:
last_played.append(item.Song.id)
# find a song not played recently
song_instance = Song.objects.exclude(
id__in=last_played
).filter(
Artist__id__in=artists
).order_by('?')[0:1].get()
return song_instance
def addToHistory(self, song_instance, user_list):
history_instance = History(
Song=song_instance
)
history_instance.save()
if user_list is not None and user_list.count() > 0:
for user_instance in user_list.all():
history_instance.User.add(user_instance)
def skipCurrentSong(self):
players = Player.objects.all()
for player in players:
try:
os.kill(player.Pid, SIGABRT)
except OSError:
player.delete()
class history(api_base):
order_by_fields = {
"title": "Song__Title",
"artist": "Song__Artist__Name",
"album": "Song__Album__Title",
"year": "Song__Year",
"genre": "Song__Genre__Name",
"created": "Created",
}
order_by_default = {
"created": "-Created",
}
def index(self, page=1):
object_list = History.objects.all()
object_list = self.source_set_order(object_list)
result = self.build_result(object_list, page)
result = self.result_set_order(result)
return result
def build_result(self, object_list, page):
# prepare result
result = self.get_default_result("history", page)
# get data
paginator = Paginator(object_list, self.count)
try:
page_obj = paginator.page(page)
except InvalidPage:
return result
result = self.result_set_order(result)
result["hasNextPage"] = page_obj.has_next()
for item in page_obj.object_list:
dataset = {
"id": item.Song.id,
"title": None,
"artist": {
"id": None,
"name": None,
},
"album": {
"id": None,
"title": None,
},
"year": None,
"genre": {
"id": None,
"name": None,
},
"queued": False,
"favourite": False,
"created": formats.date_format(
item.Created, "DATETIME_FORMAT"
),
"votes": item.User.count(),
"users": [],
}
if not item.Song.Title is None:
dataset["title"] = item.Song.Title
if not item.Song.Artist is None:
dataset["artist"]["id"] = item.Song.Artist.id
dataset["artist"]["name"] = item.Song.Artist.Name
if not item.Song.Album is None:
dataset["album"]["id"] = item.Song.Album.id
dataset["album"]["title"] = item.Song.Album.Title
if not item.Song.Year is None:
dataset["year"] = item.Song.Year
if not item.Song.Genre is None:
dataset["genre"]["id"] = item.Song.Genre.id
dataset["genre"]["name"] = item.Song.Genre.Name
if not item.User.count() == 0:
for user in item.User.all():
dataset["users"].append({
"id": user.id,
"name": user.get_full_name()
})
dataset = self.result_add_queue_and_favourite(item.Song, dataset)
result["itemList"].append(dataset)
return result
def getCurrent(self):
item = History.objects.all()[0:1].get()
createdTimestamp = time.mktime(item.Created.timetuple())
dataset = {
"id": item.Song.id,
"title": None,
"artist": {
"id": None,
"name": None,
},
"album": {
"id": None,
"title": None,
},
"year": None,
"genre": {
"id": None,
"name": None,
},
"queued": False,
"favourite": False,
"created": formats.date_format(
item.Created, "DATETIME_FORMAT"
),
"votes": item.User.count(),
"users": [],
"remaining": createdTimestamp + item.Song.Length - int(time.time())
}
if not item.Song.Title is None:
dataset["title"] = item.Song.Title
if not item.Song.Artist is None:
dataset["artist"]["id"] = item.Song.Artist.id
dataset["artist"]["name"] = item.Song.Artist.Name
if not item.Song.Album is None:
dataset["album"]["id"] = item.Song.Album.id
dataset["album"]["title"] = item.Song.Album.Title
if not item.Song.Year is None:
dataset["year"] = item.Song.Year
if not item.Song.Genre is None:
dataset["genre"]["id"] = item.Song.Genre.id
dataset["genre"]["name"] = item.Song.Genre.Name
return dataset
class history_my(history):
order_by_fields = {
"title": "Song__Title",
"artist": "Song__Artist__Name",
"album": "Song__Album__Title",
"year": "Song__Year",
"genre": "Song__Genre__Name",
"created": "Created",
}
order_by_default = {
"created": "-Created",
}
def index(self, page=1):
object_list = History.objects.all().filter(User__id=self.user_id)
object_list = self.source_set_order(object_list)
result = self.build_result(object_list, page)
result = self.result_set_order(result)
result["type"] = "history/my"
return result
class queue(api_base):
order_by_fields = {
"title": "Song__Title",
"artist": "Song__Artist__Name",
"album": "Song__Album__Title",
"year": "Song__Year",
"genre": "Song__Genre__Name",
"created": "Created",
"votes": "VoteCount",
}
order_by_default = {
"votes": "-VoteCount",
"created": "MinCreated",
}
def index(self, page=1):
object_list = Queue.objects.all()
object_list = object_list.annotate(VoteCount=Count("User"))
object_list = object_list.annotate(MinCreated=Min("Created"))
object_list = self.source_set_order(object_list)
# prepare result
result = self.get_default_result("queue", page)
# get data
paginator = Paginator(object_list, self.count)
try:
page_obj = paginator.page(page)
except InvalidPage:
return result
result = self.result_set_order(result)
result["hasNextPage"] = page_obj.has_next()
for item in page_obj.object_list:
result["itemList"].append(self.get(item.Song.id))
return result
def get(self, song_id):
song = Song.objects.get(id=song_id)
item = Queue.objects.get(Song=song)
result = {
"id": item.Song.id,
"title": None,
"artist": {
"id": None,
"name": None,
},
"album": {
"id": None,
"title": None,
},
"year": None,
"genre": {
"id": None,
"name": None,
},
"queued": False,
"favourite": False,
"created": formats.date_format(item.Created, "DATETIME_FORMAT"),
"votes": item.User.count(),
"users": [],
}
if not item.Song.Title is None:
result["title"] = item.Song.Title
if not item.Song.Artist is None:
result["artist"]["id"] = item.Song.Artist.id
result["artist"]["name"] = item.Song.Artist.Name
if not item.Song.Album is None:
result["album"]["id"] = item.Song.Album.id
result["album"]["title"] = item.Song.Album.Title
if not item.Song.Year is None:
result["year"] = item.Song.Year
if not item.Song.Genre is None:
result["genre"]["id"] = item.Song.Genre.id
result["genre"]["name"] = item.Song.Genre.Name
if not item.User.count() == 0:
for user in item.User.all():
result["users"].append({"id": user.id, "name": user.get_full_name()})
result = self.result_add_queue_and_favourite(item.Song, result)
return result
def add(self, song_id):
song = Song.objects.get(id=song_id)
user = User.objects.get(id=self.user_id)
try:
queue = Queue.objects.get(Song=song)
except ObjectDoesNotExist:
queue = Queue(
Song=song
)
queue.save()
queue.User.add(user)
return song_id
def remove(self, song_id):
song = Song.objects.get(id=song_id)
user = User.objects.get(id=self.user_id)
queue = Queue.objects.get(Song=song)
queue.User.remove(user)
vote_count = queue.User.count()
if not queue.User.count():
queue.delete()
return {
"id": song_id,
"count": vote_count,
}
class favourites(api_base):
order_by_fields = {
"title": "Song__Title",
"artist": "Song__Artist__Name",
"album": "Song__Album__Title",
"year": "Song__Year",
"genre": "Song__Genre__Name",
"created": "Created",
}
order_by_default = {
"created": "-Created",
}
def index(self, page=1):
object_list = Favourite.objects.all().filter(User__id=self.user_id)
object_list = self.source_set_order(object_list)
# prepare result
result = self.get_default_result("favourites", page)
# get data
paginator = Paginator(object_list, self.count)
try:
page_obj = paginator.page(page)
except InvalidPage:
return result
result = self.result_set_order(result)
result["hasNextPage"] = page_obj.has_next()
for item in page_obj.object_list:
result["itemList"].append(self.get(item.Song.id))
return result
def get(self, song_id):
song = Song.objects.get(id=song_id)
item = Favourite.objects.get(Song=song,User__id=self.user_id)
result = {
"id": item.Song.id,
"title": None,
"artist": {
"id": None,
"name": None,
},
"album": {
"id": None,
"title": None,
},
"year": None,
"genre": {
"id": None,
"name": None,
},
"queued": False,
"favourite": False,
"created": formats.date_format(item.Created, "DATETIME_FORMAT"),
}
if not item.Song.Title is None:
result["title"] = item.Song.Title
if not item.Song.Artist is None:
result["artist"]["id"] = item.Song.Artist.id
result["artist"]["name"] = item.Song.Artist.Name
if not item.Song.Album is None:
result["album"]["id"] = item.Song.Album.id
result["album"]["title"] = item.Song.Album.Title
if not item.Song.Year is None:
result["year"] = item.Song.Year
if not item.Song.Genre is None:
result["genre"]["id"] = item.Song.Genre.id
result["genre"]["name"] = item.Song.Genre.Name
result = self.result_add_queue_and_favourite(item.Song, result)
return result
def add(self, song_id):
song = Song.objects.get(id=song_id)
user = User.objects.get(id=self.user_id)
favourite = Favourite(
Song=song,
User=user
)
favourite.save()
return song_id
def remove(self, song_id):
song = Song.objects.get(id=song_id)
user = User.objects.get(id=self.user_id)
Favourite.objects.get(
Song=song,
User=user
).delete()
return {
"id": song_id,
}
class artists(api_base):
order_by_fields = {
"artist": "Name",
}
order_by_default = {
"artist": "Name",
}
def index(self, page=1):
# prepare result
result = self.get_default_result("artists", page)
object_list = Artist.objects.all()
object_list = self.source_set_order(object_list)
# get data
paginator = Paginator(object_list, self.count)
try:
page_obj = paginator.page(page)
except InvalidPage:
return result
result = self.result_set_order(result)
result["hasNextPage"] = page_obj.has_next()
for item in page_obj.object_list:
dataset = {
"id": item.id,
"artist": item.Name,
}
result["itemList"].append(dataset)
return result
class albums(api_base):
order_by_fields = {
"album": "Title",
}
order_by_default = {
"album": "Title",
}
def index(self, page=1):
# prepare result
result = self.get_default_result("albums", page)
object_list = Album.objects.all()
object_list = self.source_set_order(object_list)
# get data
paginator = Paginator(object_list, self.count)
try:
page_obj = paginator.page(page)
except InvalidPage:
return result
result = self.result_set_order(result)
result["hasNextPage"] = page_obj.has_next()
for item in page_obj.object_list:
dataset = {
"id": item.id,
"album": item.Title,
}
result["itemList"].append(dataset)
return result
class genres(api_base):
order_by_fields = {
"genre": "Name",
}
order_by_default = {
"genre": "Name",
}
def index(self, page=1):
# prepare result
result = self.get_default_result("genres", page)
object_list = Genre.objects.all()
object_list = self.source_set_order(object_list)
# get data
paginator = Paginator(object_list, self.count)
try:
page_obj = paginator.page(page)
except InvalidPage:
return result
result = self.result_set_order(result)
result["hasNextPage"] = page_obj.has_next()
for item in page_obj.object_list:
dataset = {
"id": item.id,
"genre": item.Name,
}
result["itemList"].append(dataset)
return result
class years(api_base):
order_by_fields = {
"year": "Year",
}
order_by_default = {
"year": "Year"
}
def index(self, page=1):
# prepare result
result = self.get_default_result("years", page)
object_list = Song.objects.values("Year").distinct()
object_list = object_list.exclude(Year=None).exclude(Year=0)
object_list = self.source_set_order(object_list)
# get data
paginator = Paginator(object_list, self.count)
try:
page_obj = paginator.page(page)
except InvalidPage:
return result
result = self.result_set_order(result)
result["hasNextPage"] = page_obj.has_next()
for item in page_obj.object_list:
dataset = {
"year": item["Year"],
}
result["itemList"].append(dataset)
return result
class players(api_base):
def add(self, pid):
player = Player(
Pid=pid
)
player.save()
return player.id
def remove(self, pid):
Player.objects.get(Pid=pid).delete()
return {
"pid": pid,
}
================================================
FILE: jukebox/jukebox_core/docs/API.rst
================================================
API
=====
Jukebox core provides a REST API for authenticated users to control the jukebox.
Please register a Django User in the admin interface and use HTTP basic authentication for external API access.
Alternatively the API is accessible for authenticated users sending a session key.
Tests
======
The API is completely unit tested. See jukebox_core/tests
GET methods
============
::
/api/v1/songs
/api/v1/artists
/api/v1/albums
/api/v1/genres
/api/v1/years
/api/v1/history
/api/v1/history/my
/api/v1/queue
/api/v1/queue/[song_id]
/api/v1/favourites
/api/v1/favourites/[song_id]
/api/v1/ping
*Sort parameters for list functions*
- order_by (field to order by, see below)
- order_direction ("asc" or "desc", defaults to "asc")
**/api/v1/songs**
List songs
*Available options*
- count (item count to be returned)
- page (page to be returned)
- search_term (search in song title, artist name and album title)
- search_title (search in song title)
- search_artist (search in artist name)
- search_album (search in album title)
- filter_artist_id (filter by artist id)
- filter_album_id (filter by album id)
- filter_genre (filter by genre id)
- filter_year (filter by release year)
*Available sort options*
- title (default, asc)
- artist
- album
- year
- genre
- length
**/api/v1/artists**
List artists
*Available sort options*
- artist (default, asc)
**/api/v1/albums**
List albums
*Available sort options*
- album (default, asc)
**/api/v1/genres**
List genres
*Available sort options*
- genre (default, asc)
**/api/v1/years**
List years
*Available sort options*
- year (default, asc)
**/api/v1/history**
List all played songs
*Available sort options*
- title
- artist
- album
- year
- genre
- created (default, desc)
**/api/v1/history/my**
List all played songs the authenticated user voted for
See /api/v1/history for details
**/api/v1/queue**
List songs in play queue sorted by vote count and first vote
*Available sort options*
- title
- artist
- album
- year
- genre
- created (default, asc)
- votes (default, desc)
**/api/v1/queue/[song_id]**
Get single play queue entry
**/api/v1/favourites**
*Available sort options*
- title (default, asc)
- artist
- album
- year
- genre
- created
**/api/v1/favourites/[song_id]**
Get single favourite list entry
**/api/v1/ping**
Ping the api for session keepalive
POST methods
============
::
/api/v1/queue
/api/v1/favourites
**/api/v1/queue**
Vote for song, add to queue if not yet in
*Required post parameters*
- id (id of song to be added)
**/api/v1/favourites**
Add song to favourite list
*Required post parameters*
- id (id of song to be added)
DELETE methods
===============
::
/api/v1/queue/[song_id]
/api/v1/favourites/[song_id]
**/api/v1/queue/[song_id]**
Revoke vote for song, remove from queue if no more votes left
**/api/v1/favourites/[song_id]**
Remove song from favourite list
================================================
FILE: jukebox/jukebox_core/forms.py
================================================
# -*- coding: UTF-8 -*-
from django import forms
class IdForm(forms.Form):
id = forms.IntegerField(
required=True
)
class SongsForm(forms.Form):
count = forms.IntegerField(
required=False
)
page = forms.IntegerField(
required=False
)
search_term = forms.CharField(
required=False
)
search_title = forms.CharField(
required=False
)
search_artist = forms.CharField(
required=False
)
search_album = forms.CharField(
required=False
)
filter_year = forms.IntegerField(
required=False
)
filter_genre = forms.IntegerField(
required=False
)
filter_album_id = forms.IntegerField(
required=False
)
filter_artist_id = forms.IntegerField(
required=False
)
order_by = forms.CharField(
max_length=6,
help_text="'title', 'artist', 'album', 'year', 'genre'",
required=False
)
order_direction = forms.CharField(
max_length=4,
help_text="'asc', 'desc'",
required=False
)
class ArtistsForm(forms.Form):
count = forms.IntegerField(
required=False
)
page = forms.IntegerField(
required=False
)
order_by = forms.CharField(
max_length=6,
help_text="'artist'",
required=False
)
order_direction = forms.CharField(
max_length=4,
help_text="'asc', 'desc'",
required=False
)
class AlbumsForm(forms.Form):
count = forms.IntegerField(
required=False
)
page = forms.IntegerField(
required=False
)
order_by = forms.CharField(
max_length=6,
help_text="'album', 'artist'",
required=False
)
order_direction = forms.CharField(
max_length=4,
help_text="'asc', 'desc'",
required=False
)
class GenresForm(forms.Form):
count = forms.IntegerField(
required=False
)
page = forms.IntegerField(
required=False
)
order_by = forms.CharField(
max_length=5,
help_text="'genre'",
required=False
)
order_direction = forms.CharField(
max_length=4,
help_text="'asc', 'desc'",
required=False
)
class YearsForm(forms.Form):
count = forms.IntegerField(
required=False
)
page = forms.IntegerField(
required=False
)
order_by = forms.CharField(
max_length=4,
help_text="'year'",
required=False
)
order_direction = forms.CharField(
max_length=4,
help_text="'asc', 'desc'",
required=False
)
class HistoryForm(forms.Form):
count = forms.IntegerField(
required=False
)
page = forms.IntegerField(
required=False
)
order_by = forms.CharField(
max_length=7,
help_text="'title', 'artist', 'album', 'year', 'genre', 'created'",
required=False
)
order_direction = forms.CharField(
max_length=4,
help_text="'asc', 'desc'",
required=False
)
class FavouritesForm(forms.Form):
count = forms.IntegerField(
required=False
)
page = forms.IntegerField(
required=False
)
order_by = forms.CharField(
max_length=7,
help_text="'title', 'artist', 'album', 'year', 'genre', 'created'",
required=False
)
order_direction = forms.CharField(
max_length=4,
help_text="'asc', 'desc'",
required=False
)
class QueueForm(forms.Form):
count = forms.IntegerField(
required=False
)
page = forms.IntegerField(
required=False
)
order_by = forms.CharField(
max_length=7,
help_text="'title', 'artist', 'album', 'year', \
'genre', 'created', 'votes'",
required=False
)
order_direction = forms.CharField(
max_length=4,
help_text="'asc', 'desc'",
required=False
)
================================================
FILE: jukebox/jukebox_core/management/__init__.py
================================================
================================================
FILE: jukebox/jukebox_core/management/commands/__init__.py
================================================
================================================
FILE: jukebox/jukebox_core/management/commands/jukebox_index.py
================================================
# -*- coding: UTF-8 -*-
from django.core.management.base import BaseCommand
from optparse import make_option
import os
from jukebox.jukebox_core.utils import FileIndexer
class Command(BaseCommand):
option_list = BaseCommand.option_list + (
make_option("--path", action="store", dest="path",
help="Music library path to scan"),
)
def handle(self, *args, **options):
if options["path"] is None:
print "Required arguments: path"
return
if not os.path.exists(options["path"]):
print "Path does not exist"
return
print "Indexing music in " + options["path"]
print "This may take a while"
self.index(options["path"], int(options["verbosity"]))
def index(self, path, verbosity):
if not path.endswith("/"):
path += "/"
indexer = FileIndexer()
listing = os.listdir(path)
for filename in listing:
filename = path + filename
if os.path.isdir(filename):
self.index(filename + "/", verbosity)
elif filename.endswith(".mp3"):
if verbosity >= 2:
print "Indexing file " + filename
indexer.index(filename)
================================================
FILE: jukebox/jukebox_core/management/commands/jukebox_setup.py
================================================
# -*- coding: UTF-8 -*-
from django.core.management.base import BaseCommand
from django.conf import settings
class Command(BaseCommand):
def handle(self, *args, **options):
print "----------------------------------------------"
print "----- Welcome to the jukebox setup -----"
print "----------------------------------------------"
print ""
print "Page administrator"
print "----------------------------------------------"
print ""
admin_user = raw_input("\tName: ")
admin_email = raw_input("\tE-mail: ")
print ""
# get authentication methods
authentication = self.setAuthentication()
while not authentication:
authentication = self.setAuthentication()
self.setup(admin_user, admin_email, authentication)
def setAuthentication(self):
print "Please select your authentication methods"
print "----------------------------------------------"
print "Available providers: Facebook, Twitter, Github"
print ""
print "Facebook"
print "\tFacebook authentication requires setup of a Facebook app on "\
"http://developers.facebook.com/setup/"
facebook = self.readAppData("Facebook")
print ""
print "Twitter"
print "\tTwitter authentication requires setup of a Twitter app on "\
"https://dev.twitter.com/apps/new"
twitter = self.readAppData("Twitter")
print ""
print "Github"
print "\tGithub authentication requires setup of a Github app on "\
"https://github.com/settings/applications/new"
github = self.readAppData("Github")
print ""
if facebook is None and twitter is None and github is None:
print "Are your kidding me? Why didn't you select a provider?"
print "I won't let you go until you select at least one of them."
print ""
return False
return {
"facebook": facebook,
"twitter": twitter,
"github": github,
}
def readAppData(self, name):
type = raw_input(
"\tUse " + name + " for authentication? [y/n] "
).strip().lower()
while type != "n" and type != "y":
type = raw_input(
"\tInvalid answer, please enter 'y' for yes or 'n' for no: "
).strip().lower()
if type == "y":
id = ""
while id.strip() == "":
id = raw_input("\t" + name + " app id: ")
if id.strip() == "":
print "\t\tInvald app id"
secret = ""
while secret.strip() == "":
secret = raw_input("\t" + name + " app secret: ")
if secret.strip() == "":
print "\t\tInvald app id"
return {
"id": id,
"secret": secret,
}
return None
def setup(self, admin_user, admin_email, authentication):
setup = open(settings.BASE_DIR + "/settings_local.example.py").read()
setup = setup.replace("[admin_user]", admin_user)
setup = setup.replace("[admin_email]", admin_email)
auth_backends = []
auth_backends_enabled = []
if authentication["facebook"] is not None:
auth_backends.append(
"\"social_auth.backends.facebook.FacebookBackend\","
)
auth_backends_enabled.append("\"facebook\",")
if authentication["twitter"] is not None:
auth_backends.append(
"\"social_auth.backends.twitter.TwitterBackend\","
)
auth_backends_enabled.append("\"twitter\",")
if authentication["github"] is not None:
auth_backends.append(
"\"social_auth.backends.contrib.github.GithubBackend\","
)
auth_backends_enabled.append("\"github\",")
setup = setup.replace(
"[auth_backends]",
"\n ".join(auth_backends)
)
setup = setup.replace(
"[auth_backends_enabled]",
" ".join(auth_backends_enabled)
)
auth_data = ""
for key, value in authentication.items():
if value is None:
continue
if key == 'twitter':
auth_data += "TWITTER_CONSUMER_KEY = \"" + \
value["id"] + "\"\n"
auth_data += "TWITTER_CONSUMER_SECRET = \"" + \
value["secret"] + "\"\n"
else:
auth_data += key.upper() + "_APP_ID = \"" + \
value["id"] + "\"\n"
auth_data += key.upper() + "_API_SECRET = \"" + \
value["secret"] + "\"\n"
auth_data += "\n"
setup = setup.replace("[auth_data]", auth_data)
f = open(settings.JUKEBOX_STORAGE_PATH + "/settings_local.py", "w+")
f.write(setup)
f.close()
print "Setup finished"
print "----------------------------------------------"
================================================
FILE: jukebox/jukebox_core/migrations/0001_initial.py
================================================
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'Artist'
db.create_table('jukebox_core_artist', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('Name', self.gf('django.db.models.fields.CharField')(max_length=200)),
))
db.send_create_signal('jukebox_core', ['Artist'])
# Adding model 'Genre'
db.create_table('jukebox_core_genre', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('Name', self.gf('django.db.models.fields.CharField')(max_length=200)),
))
db.send_create_signal('jukebox_core', ['Genre'])
# Adding model 'Album'
db.create_table('jukebox_core_album', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('Title', self.gf('django.db.models.fields.CharField')(max_length=200)),
('Artist', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['jukebox_core.Artist'])),
))
db.send_create_signal('jukebox_core', ['Album'])
# Adding model 'Song'
db.create_table('jukebox_core_song', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('Artist', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['jukebox_core.Artist'])),
('Album', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['jukebox_core.Album'], null=True)),
('Genre', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['jukebox_core.Genre'], null=True)),
('Title', self.gf('django.db.models.fields.CharField')(max_length=200)),
('Year', self.gf('django.db.models.fields.IntegerField')(null=True)),
('Length', self.gf('django.db.models.fields.IntegerField')()),
('Filename', self.gf('django.db.models.fields.CharField')(max_length=1000)),
))
db.send_create_signal('jukebox_core', ['Song'])
# Adding model 'Queue'
db.create_table('jukebox_core_queue', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('Song', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['jukebox_core.Song'], unique=True)),
('Created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
))
db.send_create_signal('jukebox_core', ['Queue'])
# Adding M2M table for field User on 'Queue'
db.create_table('jukebox_core_queue_User', (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('queue', models.ForeignKey(orm['jukebox_core.queue'], null=False)),
('user', models.ForeignKey(orm['auth.user'], null=False))
))
db.create_unique('jukebox_core_queue_User', ['queue_id', 'user_id'])
# Adding model 'Favourite'
db.create_table('jukebox_core_favourite', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('Song', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['jukebox_core.Song'])),
('User', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
('Created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
))
db.send_create_signal('jukebox_core', ['Favourite'])
# Adding unique constraint on 'Favourite', fields ['Song', 'User']
db.create_unique('jukebox_core_favourite', ['Song_id', 'User_id'])
# Adding model 'History'
db.create_table('jukebox_core_history', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('Song', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['jukebox_core.Song'])),
('Created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
))
db.send_create_signal('jukebox_core', ['History'])
# Adding M2M table for field User on 'History'
db.create_table('jukebox_core_history_User', (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('history', models.ForeignKey(orm['jukebox_core.history'], null=False)),
('user', models.ForeignKey(orm['auth.user'], null=False))
))
db.create_unique('jukebox_core_history_User', ['history_id', 'user_id'])
# Adding model 'Player'
db.create_table('jukebox_core_player', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('Pid', self.gf('django.db.models.fields.IntegerField')()),
))
db.send_create_signal('jukebox_core', ['Player'])
def backwards(self, orm):
# Removing unique constraint on 'Favourite', fields ['Song', 'User']
db.delete_unique('jukebox_core_favourite', ['Song_id', 'User_id'])
# Deleting model 'Artist'
db.delete_table('jukebox_core_artist')
# Deleting model 'Genre'
db.delete_table('jukebox_core_genre')
# Deleting model 'Album'
db.delete_table('jukebox_core_album')
# Deleting model 'Song'
db.delete_table('jukebox_core_song')
# Deleting model 'Queue'
db.delete_table('jukebox_core_queue')
# Removing M2M table for field User on 'Queue'
db.delete_table('jukebox_core_queue_User')
# Deleting model 'Favourite'
db.delete_table('jukebox_core_favourite')
# Deleting model 'History'
db.delete_table('jukebox_core_history')
# Removing M2M table for field User on 'History'
db.delete_table('jukebox_core_history_User')
# Deleting model 'Player'
db.delete_table('jukebox_core_player')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'jukebox_core.album': {
'Artist': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['jukebox_core.Artist']"}),
'Meta': {'ordering': "['Title']", 'object_name': 'Album'},
'Title': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'jukebox_core.artist': {
'Meta': {'ordering': "['Name']", 'object_name': 'Artist'},
'Name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'jukebox_core.favourite': {
'Created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'Meta': {'ordering': "['-Created']", 'unique_together': "(('Song', 'User'),)", 'object_name': 'Favourite'},
'Song': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['jukebox_core.Song']"}),
'User': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'jukebox_core.genre': {
'Meta': {'ordering': "['Name']", 'object_name': 'Genre'},
'Name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'jukebox_core.history': {
'Created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'Meta': {'ordering': "['-Created']", 'object_name': 'History'},
'Song': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['jukebox_core.Song']"}),
'User': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'null': 'True', 'symmetrical': 'False'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'jukebox_core.player': {
'Meta': {'object_name': 'Player'},
'Pid': ('django.db.models.fields.IntegerField', [], {}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'jukebox_core.queue': {
'Created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'Meta': {'object_name': 'Queue'},
'Song': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['jukebox_core.Song']", 'unique': 'True'}),
'User': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'symmetrical': 'False'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'jukebox_core.song': {
'Album': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['jukebox_core.Album']", 'null': 'True'}),
'Artist': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['jukebox_core.Artist']"}),
'Filename': ('django.db.models.fields.CharField', [], {'max_length': '1000'}),
'Genre': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['jukebox_core.Genre']", 'null': 'True'}),
'Length': ('django.db.models.fields.IntegerField', [], {}),
'Meta': {'ordering': "['Title', 'Artist', 'Album']", 'object_name': 'Song'},
'Title': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'Year': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
}
}
complete_apps = ['jukebox_core']
================================================
FILE: jukebox/jukebox_core/migrations/0002_auto__del_field_album_Artist.py
================================================
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Deleting field 'Album.Artist'
db.delete_column('jukebox_core_album', 'Artist_id')
def backwards(self, orm):
# User chose to not deal with backwards NULL issues for 'Album.Artist'
raise RuntimeError("Cannot reverse this migration. 'Album.Artist' and its values cannot be restored.")
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'jukebox_core.album': {
'Meta': {'ordering': "['Title']", 'object_name': 'Album'},
'Title': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'jukebox_core.artist': {
'Meta': {'ordering': "['Name']", 'object_name': 'Artist'},
'Name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'jukebox_core.favourite': {
'Created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'Meta': {'ordering': "['-Created']", 'unique_together': "(('Song', 'User'),)", 'object_name': 'Favourite'},
'Song': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['jukebox_core.Song']"}),
'User': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'jukebox_core.genre': {
'Meta': {'ordering': "['Name']", 'object_name': 'Genre'},
'Name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'jukebox_core.history': {
'Created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'Meta': {'ordering': "['-Created']", 'object_name': 'History'},
'Song': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['jukebox_core.Song']"}),
'User': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'null': 'True', 'symmetrical': 'False'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'jukebox_core.player': {
'Meta': {'object_name': 'Player'},
'Pid': ('django.db.models.fields.IntegerField', [], {}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'jukebox_core.queue': {
'Created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'Meta': {'object_name': 'Queue'},
'Song': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['jukebox_core.Song']", 'unique': 'True'}),
'User': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'symmetrical': 'False'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'jukebox_core.song': {
'Album': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['jukebox_core.Album']", 'null': 'True'}),
'Artist': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['jukebox_core.Artist']"}),
'Filename': ('django.db.models.fields.CharField', [], {'max_length': '1000'}),
'Genre': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['jukebox_core.Genre']", 'null': 'True'}),
'Length': ('django.db.models.fields.IntegerField', [], {}),
'Meta': {'ordering': "['Title', 'Artist', 'Album']", 'object_name': 'Song'},
'Title': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'Year': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
}
}
complete_apps = ['jukebox_core']
================================================
FILE: jukebox/jukebox_core/migrations/__init__.py
================================================
================================================
FILE: jukebox/jukebox_core/models.py
================================================
# -*- coding: UTF-8 -*-
from django.db import models
from django.contrib.auth.models import User
from django.contrib.syndication.views import Feed
import time
class Artist(models.Model):
class Meta:
ordering = ['Name']
def __unicode__(self):
return "%s" % self.Name
Name = models.CharField(max_length=200)
class Genre(models.Model):
class Meta:
ordering = ['Name']
def __unicode__(self):
return "%s" % self.Name
Name = models.CharField(max_length=200)
class Album(models.Model):
class Meta:
ordering = ['Title']
def __unicode__(self):
return "%s" % self.Title
Title = models.CharField(max_length=200)
class Song(models.Model):
class Meta:
ordering = ['Title', 'Artist', 'Album']
def __unicode__(self):
return "%s - %s" % (self.Artist.Name, self.Title)
Artist = models.ForeignKey(Artist)
Album = models.ForeignKey(Album, null=True)
Genre = models.ForeignKey(Genre, null=True)
Title = models.CharField(max_length=200)
Year = models.IntegerField(null=True)
Length = models.IntegerField()
Filename = models.CharField(max_length=1000)
class Queue(models.Model):
Song = models.ForeignKey(Song, unique=True)
User = models.ManyToManyField(User)
Created = models.DateTimeField(auto_now_add=True)
class Favourite(models.Model):
class Meta:
unique_together = ("Song", "User")
ordering = ['-Created']
Song = models.ForeignKey(Song)
User = models.ForeignKey(User)
Created = models.DateTimeField(auto_now_add=True)
class History(models.Model):
class Meta:
ordering = ['-Created']
Song = models.ForeignKey(Song)
User = models.ManyToManyField(User, null=True)
Created = models.DateTimeField(auto_now_add=True)
class Player(models.Model):
Pid = models.IntegerField()
class QueueFeed(Feed):
title = "Jukebox Queue Feed"
link = "/queue/"
description = "Top song in the queue"
def items(self):
return Queue.objects.all()[:1]
def item_title(self, item):
return item.Song.Title
def item_description(self, item):
return unicode(item.Song.Title) + " by " + \
unicode(item.Song.Artist) + " from " + \
unicode(item.Song.Album)
def item_link(self, item):
# Not sure what to do with url as there isn't any unque url for song
return "/queue/#" + unicode(int(round(time.time() * 1000)))
================================================
FILE: jukebox/jukebox_core/tests/__init__.py
================================================
# -*- coding: UTF-8 -*-
from .api_songs import *
from .api_artists import *
from .api_albums import *
from .api_genres import *
from .api_years import *
from .api_favourites import *
from .api_history import *
from .api_queue import *
================================================
FILE: jukebox/jukebox_core/tests/api.py
================================================
# -*- coding: UTF-8 -*-
from django.test import TestCase, Client
from django.db import transaction
import base64
from jukebox.jukebox_core.models import Artist, Album, Genre, Song
from django.contrib.auth.models import User
class ApiTestBase(TestCase):
user = None
username = "TestUser"
email = "test@domain.org"
password = "TestPassword"
passwords = {}
def setUp(self):
transaction.rollback()
# register test user and setup auth
self.user = self.addUser(self.username, self.email, self.password)
def httpGet(self, url, params={}, user=None):
c = Client()
return c.get(url, params, HTTP_AUTHORIZATION=self.getAuth(user))
def httpPost(self, url, params={}, user=None):
c = Client()
return c.post(url, params, HTTP_AUTHORIZATION=self.getAuth(user))
def httpDelete(self, url, params={}, user=None):
c = Client()
return c.delete(url, params, HTTP_AUTHORIZATION=self.getAuth(user))
def getAuth(self, user=None):
if user is None:
user = self.user
username = user.username
password = self.passwords[user.id]
return "Basic %s" % base64.encodestring(
'%s:%s' % (username, password)
).strip()
def addArtist(self, name="TestArist"):
artist = Artist(
Name=name
)
artist.save()
return artist
def addAlbum(self, title="TestTitle"):
album = Album(
Title=title
)
album.save()
return album
def addGenre(self, name="TestGenre"):
genre = Genre(
Name=name
)
genre.save()
return genre
def addSong(
self,
artist,
album = None,
genre = None,
title="TestTitle",
year=2000,
length=100,
filename="/path/to/test.mp3"
):
# save a song
song = Song(
Artist=artist,
Album=album,
Genre=genre,
Title=title,
Year=year,
Length=length,
Filename=filename
)
song.save()
return song
def addUser(self, username, email, password):
user = User.objects.create_user(username, email, password)
self.passwords[user.id] = password
return user
================================================
FILE: jukebox/jukebox_core/tests/api_albums.py
================================================
# -*- coding: UTF-8 -*-
import simplejson
from jukebox.jukebox_core.tests.api import ApiTestBase
class ApiAlbumsTest(ApiTestBase):
def testIndexEmpty(self):
result = simplejson.loads(
self.httpGet(
"/api/v1/albums"
).content
)
self.assertEquals(len(result["itemList"]), 0)
def testIndex(self):
album = self.addAlbum()
result = simplejson.loads(
self.httpGet(
"/api/v1/albums"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], album.id)
def testIndexOrderByAlbum(self):
album_a = self.addAlbum(title="A Title")
album_b = self.addAlbum(title="B Title")
result = simplejson.loads(
self.httpGet(
"/api/v1/albums?order_by=album"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], album_a.id)
self.assertEquals(result["itemList"][1]["id"], album_b.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/albums?order_by=album&order_direction=desc"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], album_b.id)
self.assertEquals(result["itemList"][1]["id"], album_a.id)
def testCount(self):
album_a = self.addAlbum("AAA")
album_b = self.addAlbum("BBB")
album_c = self.addAlbum("CCC")
result = simplejson.loads(
self.httpGet(
"/api/v1/albums?count=1"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], album_a.id)
self.assertTrue(result["hasNextPage"])
result = simplejson.loads(
self.httpGet(
"/api/v1/albums?count=3"
).content
)
self.assertEquals(len(result["itemList"]), 3)
self.assertEquals(result["itemList"][0]["id"], album_a.id)
self.assertEquals(result["itemList"][1]["id"], album_b.id)
self.assertEquals(result["itemList"][2]["id"], album_c.id)
self.assertFalse(result["hasNextPage"])
def testCountAndPage(self):
album_a = self.addAlbum("AAA")
album_b = self.addAlbum("BBB")
album_c = self.addAlbum("CCC")
result = simplejson.loads(
self.httpGet(
"/api/v1/albums?count=1&page=1"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], album_a.id)
self.assertTrue(result["hasNextPage"])
result = simplejson.loads(
self.httpGet(
"/api/v1/albums?count=1&page=2"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], album_b.id)
self.assertTrue(result["hasNextPage"])
result = simplejson.loads(
self.httpGet(
"/api/v1/albums?count=1&page=3"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], album_c.id)
self.assertFalse(result["hasNextPage"])
================================================
FILE: jukebox/jukebox_core/tests/api_artists.py
================================================
# -*- coding: UTF-8 -*-
import simplejson
from jukebox.jukebox_core.tests.api import ApiTestBase
class ApiArtistsTest(ApiTestBase):
def testIndexEmpty(self):
result = simplejson.loads(
self.httpGet(
"/api/v1/artists"
).content
)
self.assertEquals(len(result["itemList"]), 0)
def testIndex(self):
artist = self.addArtist()
result = simplejson.loads(
self.httpGet(
"/api/v1/artists"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], artist.id)
def testIndexOrderBy(self):
artist_a = self.addArtist(name="A Name")
artist_b = self.addArtist(name="B Name")
result = simplejson.loads(
self.httpGet(
"/api/v1/artists?order_by=artist"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], artist_a.id)
self.assertEquals(result["itemList"][1]["id"], artist_b.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/artists?order_by=artist&order_direction=desc"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], artist_b.id)
self.assertEquals(result["itemList"][1]["id"], artist_a.id)
def testCount(self):
artist_a = self.addArtist()
artist_b = self.addArtist()
artist_c = self.addArtist()
result = simplejson.loads(
self.httpGet(
"/api/v1/artists?count=1"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], artist_a.id)
self.assertTrue(result["hasNextPage"])
result = simplejson.loads(
self.httpGet(
"/api/v1/artists?count=3"
).content
)
self.assertEquals(len(result["itemList"]), 3)
self.assertEquals(result["itemList"][0]["id"], artist_a.id)
self.assertEquals(result["itemList"][1]["id"], artist_b.id)
self.assertEquals(result["itemList"][2]["id"], artist_c.id)
self.assertFalse(result["hasNextPage"])
def testCountAndPage(self):
artist_a = self.addArtist()
artist_b = self.addArtist()
artist_c = self.addArtist()
result = simplejson.loads(
self.httpGet(
"/api/v1/artists?count=1&page=1"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], artist_a.id)
self.assertTrue(result["hasNextPage"])
result = simplejson.loads(
self.httpGet(
"/api/v1/artists?count=1&page=2"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], artist_b.id)
self.assertTrue(result["hasNextPage"])
result = simplejson.loads(
self.httpGet(
"/api/v1/artists?count=1&page=3"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], artist_c.id)
self.assertFalse(result["hasNextPage"])
================================================
FILE: jukebox/jukebox_core/tests/api_favourites.py
================================================
# -*- coding: UTF-8 -*-
import simplejson
from jukebox.jukebox_core.tests.api import ApiTestBase
# ATTENTION: order tests
# favourites are ordered by insertion date DESC per default
class ApiFavouritesTest(ApiTestBase):
def testIndexEmpty(self):
result = simplejson.loads(
self.httpGet(
"/api/v1/favourites"
).content
)
self.assertEquals(len(result["itemList"]), 0)
def testAddAndIndex(self):
# register second user
user = self.addUser("TestUser2", "test2@domain.org", "TestPassword2")
song = self.addSong(artist=self.addArtist())
# check that song is not a favourite
result = simplejson.loads(
self.httpGet(
"/api/v1/songs"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song.id)
self.assertFalse(result["itemList"][0]["favourite"])
# add to favourites
response = self.httpPost(
"/api/v1/favourites",
{"id": song.id}
)
content = simplejson.loads(
response.content
)
self.assertEqual(response.status_code, 201)
self.assertEqual(content["id"], song.id)
# check favourites list
result = simplejson.loads(
self.httpGet(
"/api/v1/favourites"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song.id)
# check that song is marked as favourite
result = simplejson.loads(
self.httpGet(
"/api/v1/songs"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song.id)
self.assertTrue(result["itemList"][0]["favourite"])
# check favourites list of second user
result = simplejson.loads(
self.httpGet(
"/api/v1/favourites",
{},
user
).content
)
self.assertEquals(len(result["itemList"]), 0)
# check that song is not marked as favourite for second user
result = simplejson.loads(
self.httpGet(
"/api/v1/songs",
{},
user
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song.id)
self.assertFalse(result["itemList"][0]["favourite"])
def testDeleteAndIndex(self):
song = self.addSong(artist=self.addArtist())
# add to favourites
response = self.httpPost(
"/api/v1/favourites",
{"id": song.id}
)
content = simplejson.loads(
response.content
)
self.assertEqual(response.status_code, 201)
self.assertEqual(content["id"], song.id)
# check favourites list
result = simplejson.loads(
self.httpGet(
"/api/v1/favourites"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song.id)
# remove from favourites
response = self.httpDelete(
"/api/v1/favourites/" + str(song.id),
)
content = simplejson.loads(
response.content
)
self.assertEqual(response.status_code, 200)
self.assertEqual(content["id"], str(song.id))
# check favourites list
result = simplejson.loads(
self.httpGet(
"/api/v1/favourites"
).content
)
self.assertEquals(len(result["itemList"]), 0)
def addFavourite(self, song):
return self.httpPost(
"/api/v1/favourites",
{"id": song.id}
)
def testIndexOrderByTitle(self):
song_a = self.addSong(artist=self.addArtist(), title="A Title")
song_b = self.addSong(artist=self.addArtist(), title="B Title")
self.addFavourite(song_a)
self.addFavourite(song_b)
result = simplejson.loads(
self.httpGet(
"/api/v1/favourites?order_by=title"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertEquals(result["itemList"][1]["id"], song_b.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/favourites?order_by=title&order_direction=desc"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_b.id)
self.assertEquals(result["itemList"][1]["id"], song_a.id)
def testIndexOrderByArtist(self):
song_a = self.addSong(artist=self.addArtist(name="A Name"))
song_b = self.addSong(artist=self.addArtist(name="B Name"))
self.addFavourite(song_a)
self.addFavourite(song_b)
result = simplejson.loads(
self.httpGet(
"/api/v1/favourites?order_by=artist"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertEquals(result["itemList"][1]["id"], song_b.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/favourites?order_by=artist&order_direction=desc"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_b.id)
self.assertEquals(result["itemList"][1]["id"], song_a.id)
def testIndexOrderByAlbum(self):
album_a = self.addAlbum("A Title")
album_b = self.addAlbum("B Title")
song_a = self.addSong(artist=self.addArtist(), album=album_a)
song_b = self.addSong(artist=self.addArtist(), album=album_b)
self.addFavourite(song_a)
self.addFavourite(song_b)
result = simplejson.loads(
self.httpGet(
"/api/v1/favourites?order_by=album"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertEquals(result["itemList"][1]["id"], song_b.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/favourites?order_by=album&order_direction=desc"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_b.id)
self.assertEquals(result["itemList"][1]["id"], song_a.id)
def testIndexOrderByYear(self):
song_a = self.addSong(artist=self.addArtist(), year=2000)
song_b = self.addSong(artist=self.addArtist(), year=2001)
self.addFavourite(song_a)
self.addFavourite(song_b)
result = simplejson.loads(
self.httpGet(
"/api/v1/favourites?order_by=year"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertEquals(result["itemList"][1]["id"], song_b.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/favourites?order_by=year&order_direction=desc"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_b.id)
self.assertEquals(result["itemList"][1]["id"], song_a.id)
def testIndexOrderByGenre(self):
song_a = self.addSong(
artist=self.addArtist(),
genre=self.addGenre(name="A Genre")
)
song_b = self.addSong(
artist=self.addArtist(),
genre=self.addGenre(name="B Genre")
)
self.addFavourite(song_a)
self.addFavourite(song_b)
result = simplejson.loads(
self.httpGet(
"/api/v1/favourites?order_by=genre"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertEquals(result["itemList"][1]["id"], song_b.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/favourites?order_by=genre&order_direction=desc"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_b.id)
self.assertEquals(result["itemList"][1]["id"], song_a.id)
def testIndexOrderByCreated(self):
song_a = self.addSong(artist=self.addArtist())
song_b = self.addSong(artist=self.addArtist())
self.addFavourite(song_a)
self.addFavourite(song_b)
result = simplejson.loads(
self.httpGet(
"/api/v1/favourites?order_by=created"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertEquals(result["itemList"][1]["id"], song_b.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/favourites?order_by=created&order_direction=desc"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_b.id)
self.assertEquals(result["itemList"][1]["id"], song_a.id)
def testCount(self):
song_a = self.addSong(artist=self.addArtist())
song_b = self.addSong(artist=self.addArtist())
song_c = self.addSong(artist=self.addArtist())
self.addFavourite(song_a)
self.addFavourite(song_b)
self.addFavourite(song_c)
result = simplejson.loads(
self.httpGet(
"/api/v1/favourites?count=1"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song_c.id)
self.assertTrue(result["hasNextPage"])
result = simplejson.loads(
self.httpGet(
"/api/v1/favourites?count=3"
).content
)
self.assertEquals(len(result["itemList"]), 3)
self.assertEquals(result["itemList"][0]["id"], song_c.id)
self.assertEquals(result["itemList"][1]["id"], song_b.id)
self.assertEquals(result["itemList"][2]["id"], song_a.id)
self.assertFalse(result["hasNextPage"])
def testCountAndPage(self):
song_a = self.addSong(artist=self.addArtist())
song_b = self.addSong(artist=self.addArtist())
song_c = self.addSong(artist=self.addArtist())
self.addFavourite(song_a)
self.addFavourite(song_b)
self.addFavourite(song_c)
result = simplejson.loads(
self.httpGet(
"/api/v1/favourites?count=1&page=1"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song_c.id)
self.assertTrue(result["hasNextPage"])
result = simplejson.loads(
self.httpGet(
"/api/v1/favourites?count=1&page=2"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song_b.id)
self.assertTrue(result["hasNextPage"])
result = simplejson.loads(
self.httpGet(
"/api/v1/favourites?count=1&page=3"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertFalse(result["hasNextPage"])
================================================
FILE: jukebox/jukebox_core/tests/api_genres.py
================================================
# -*- coding: UTF-8 -*-
import simplejson
from jukebox.jukebox_core.tests.api import ApiTestBase
class ApiGenresTest(ApiTestBase):
def testIndexEmpty(self):
result = simplejson.loads(
self.httpGet(
"/api/v1/genres"
).content
)
self.assertEquals(len(result["itemList"]), 0)
def testIndex(self):
genre = self.addGenre()
result = simplejson.loads(
self.httpGet(
"/api/v1/genres"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], genre.id)
def testIndexOrderBy(self):
genre_a = self.addGenre(name="A Name")
genre_b = self.addGenre(name="B Name")
result = simplejson.loads(
self.httpGet(
"/api/v1/genres?order_by=genre"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], genre_a.id)
self.assertEquals(result["itemList"][1]["id"], genre_b.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/genres?order_by=genre&order_direction=desc"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], genre_b.id)
self.assertEquals(result["itemList"][1]["id"], genre_a.id)
def testCount(self):
genre_a = self.addGenre()
genre_b = self.addGenre()
genre_c = self.addGenre()
result = simplejson.loads(
self.httpGet(
"/api/v1/genres?count=1"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], genre_a.id)
self.assertTrue(result["hasNextPage"])
result = simplejson.loads(
self.httpGet(
"/api/v1/genres?count=3"
).content
)
self.assertEquals(len(result["itemList"]), 3)
self.assertEquals(result["itemList"][0]["id"], genre_a.id)
self.assertEquals(result["itemList"][1]["id"], genre_b.id)
self.assertEquals(result["itemList"][2]["id"], genre_c.id)
self.assertFalse(result["hasNextPage"])
def testCountAndPage(self):
genre_a = self.addGenre()
genre_b = self.addGenre()
genre_c = self.addGenre()
result = simplejson.loads(
self.httpGet(
"/api/v1/genres?count=1&page=1"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], genre_a.id)
self.assertTrue(result["hasNextPage"])
result = simplejson.loads(
self.httpGet(
"/api/v1/genres?count=1&page=2"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], genre_b.id)
self.assertTrue(result["hasNextPage"])
result = simplejson.loads(
self.httpGet(
"/api/v1/genres?count=1&page=3"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], genre_c.id)
self.assertFalse(result["hasNextPage"])
================================================
FILE: jukebox/jukebox_core/tests/api_history.py
================================================
# -*- coding: UTF-8 -*-
import simplejson
from jukebox.jukebox_core import api
from jukebox.jukebox_core.tests.api import ApiTestBase
# ATTENTION: order tests
# favourites are ordered by insertion date DESC per default
class ApiHistoryTest(ApiTestBase):
def testIndexEmpty(self):
result = simplejson.loads(
self.httpGet(
"/api/v1/history"
).content
)
self.assertEquals(len(result["itemList"]), 0)
def addSongToQueue(self, song, user=None):
if user is None:
user = self.user
return self.httpPost(
"/api/v1/queue",
{"id": song.id},
user
)
def getNextSong(self):
songs_api = api.songs()
return songs_api.getNextSong()
def testAddAndIndex(self):
song = self.addSong(artist=self.addArtist(), filename= __file__)
# check that song is not in history
result = simplejson.loads(
self.httpGet(
"/api/v1/history"
).content
)
self.assertEquals(len(result["itemList"]), 0)
# add to queue and play the song
self.addSongToQueue(song)
self.getNextSong()
# check history
result = simplejson.loads(
self.httpGet(
"/api/v1/history"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song.id)
def testAddAndIndexMy(self):
# register second user
user = self.addUser("TestUser2", "test2@domain.org", "TestPassword2")
song_a = self.addSong(artist=self.addArtist(), filename=__file__)
song_b = self.addSong(artist=self.addArtist(), filename=__file__)
# check that song is not in history
result = simplejson.loads(
self.httpGet(
"/api/v1/history/my"
).content
)
self.assertEquals(len(result["itemList"]), 0)
# add to queue and play the song
self.addSongToQueue(song_a, user)
self.addSongToQueue(song_b)
self.getNextSong()
# overall history contains the song
result = simplejson.loads(
self.httpGet(
"/api/v1/history"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
# my history should still be empty
result = simplejson.loads(
self.httpGet(
"/api/v1/history/my"
).content
)
self.assertEquals(len(result["itemList"]), 0)
# play my song
self.getNextSong()
# overall history contains both songs
result = simplejson.loads(
self.httpGet(
"/api/v1/history"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_b.id)
self.assertEquals(result["itemList"][1]["id"], song_a.id)
# check my history
result = simplejson.loads(
self.httpGet(
"/api/v1/history/my"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song_b.id)
def testIndexOrderByTitle(self):
song_a = self.addSong(
artist=self.addArtist(), title="A Title", filename=__file__
)
song_b = self.addSong(
artist=self.addArtist(), title="B Title", filename=__file__
)
self.addSongToQueue(song_a)
self.addSongToQueue(song_b)
self.getNextSong()
self.getNextSong()
result = simplejson.loads(
self.httpGet(
"/api/v1/history?order_by=title"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertEquals(result["itemList"][1]["id"], song_b.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/history/my?order_by=title"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertEquals(result["itemList"][1]["id"], song_b.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/history?order_by=title&order_direction=desc"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_b.id)
self.assertEquals(result["itemList"][1]["id"], song_a.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/history/my?order_by=title&order_direction=desc"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_b.id)
self.assertEquals(result["itemList"][1]["id"], song_a.id)
def testIndexOrderByArtist(self):
song_a = self.addSong(
artist=self.addArtist("A Name"), filename=__file__
)
song_b = self.addSong(
artist=self.addArtist("B Name"), filename=__file__
)
self.addSongToQueue(song_a)
self.addSongToQueue(song_b)
self.getNextSong()
self.getNextSong()
result = simplejson.loads(
self.httpGet(
"/api/v1/history?order_by=artist"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertEquals(result["itemList"][1]["id"], song_b.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/history/my?order_by=artist"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertEquals(result["itemList"][1]["id"], song_b.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/history?order_by=artist&order_direction=desc"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_b.id)
self.assertEquals(result["itemList"][1]["id"], song_a.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/history/my?order_by=artist&order_direction=desc"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_b.id)
self.assertEquals(result["itemList"][1]["id"], song_a.id)
def testIndexOrderByAlbum(self):
artist = self.addArtist()
song_a = self.addSong(
artist=artist,
album=self.addAlbum(title="A Title"),
filename=__file__
)
song_b = self.addSong(
artist=artist,
album=self.addAlbum(title="B Title"),
filename=__file__
)
self.addSongToQueue(song_a)
self.addSongToQueue(song_b)
self.getNextSong()
self.getNextSong()
result = simplejson.loads(
self.httpGet(
"/api/v1/history?order_by=album"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertEquals(result["itemList"][1]["id"], song_b.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/history/my?order_by=album"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertEquals(result["itemList"][1]["id"], song_b.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/history?order_by=album&order_direction=desc"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_b.id)
self.assertEquals(result["itemList"][1]["id"], song_a.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/history/my?order_by=album&order_direction=desc"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_b.id)
self.assertEquals(result["itemList"][1]["id"], song_a.id)
def testIndexOrderByYear(self):
song_a = self.addSong(
artist=self.addArtist(), filename=__file__
)
song_b = self.addSong(
artist=self.addArtist(), year=2010, filename=__file__
)
self.addSongToQueue(song_a)
self.addSongToQueue(song_b)
self.getNextSong()
self.getNextSong()
result = simplejson.loads(
self.httpGet(
"/api/v1/history?order_by=year"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertEquals(result["itemList"][1]["id"], song_b.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/history/my?order_by=year"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertEquals(result["itemList"][1]["id"], song_b.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/history?order_by=year&order_direction=desc"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_b.id)
self.assertEquals(result["itemList"][1]["id"], song_a.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/history/my?order_by=year&order_direction=desc"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_b.id)
self.assertEquals(result["itemList"][1]["id"], song_a.id)
def testIndexOrderByGenre(self):
song_a = self.addSong(
artist=self.addArtist(),
genre=self.addGenre(name="A Name"),
filename=__file__
)
song_b = self.addSong(
artist=self.addArtist(),
genre=self.addGenre(name="B Name"),
filename=__file__
)
self.addSongToQueue(song_a)
self.addSongToQueue(song_b)
self.getNextSong()
self.getNextSong()
result = simplejson.loads(
self.httpGet(
"/api/v1/history?order_by=genre"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertEquals(result["itemList"][1]["id"], song_b.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/history/my?order_by=genre"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertEquals(result["itemList"][1]["id"], song_b.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/history?order_by=genre&order_direction=desc"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_b.id)
self.assertEquals(result["itemList"][1]["id"], song_a.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/history/my?order_by=genre&order_direction=desc"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_b.id)
self.assertEquals(result["itemList"][1]["id"], song_a.id)
def testIndexOrderByCreated(self):
song_a = self.addSong(
artist=self.addArtist(),
filename=__file__
)
song_b = self.addSong(
artist=self.addArtist(),
filename=__file__
)
self.addSongToQueue(song_a)
self.addSongToQueue(song_b)
self.getNextSong()
self.getNextSong()
result = simplejson.loads(
self.httpGet(
"/api/v1/history?order_by=created"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertEquals(result["itemList"][1]["id"], song_b.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/history/my?order_by=created"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertEquals(result["itemList"][1]["id"], song_b.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/history?order_by=created&order_direction=desc"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_b.id)
self.assertEquals(result["itemList"][1]["id"], song_a.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/history/my?order_by=created&order_direction=desc"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_b.id)
self.assertEquals(result["itemList"][1]["id"], song_a.id)
def testCount(self):
song_a = self.addSong(
artist=self.addArtist(),
filename=__file__
)
song_b = self.addSong(
artist=self.addArtist(),
filename=__file__
)
song_c = self.addSong(
artist=self.addArtist(),
filename=__file__
)
self.addSongToQueue(song_a)
self.addSongToQueue(song_b)
self.addSongToQueue(song_c)
self.getNextSong()
self.getNextSong()
self.getNextSong()
result = simplejson.loads(
self.httpGet(
"/api/v1/history?count=1"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song_c.id)
self.assertTrue(result["hasNextPage"])
result = simplejson.loads(
self.httpGet(
"/api/v1/history/my?count=1"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song_c.id)
self.assertTrue(result["hasNextPage"])
result = simplejson.loads(
self.httpGet(
"/api/v1/history?count=3"
).content
)
self.assertEquals(len(result["itemList"]), 3)
self.assertEquals(result["itemList"][0]["id"], song_c.id)
self.assertEquals(result["itemList"][1]["id"], song_b.id)
self.assertEquals(result["itemList"][2]["id"], song_a.id)
self.assertFalse(result["hasNextPage"])
result = simplejson.loads(
self.httpGet(
"/api/v1/history/my?count=3"
).content
)
self.assertEquals(len(result["itemList"]), 3)
self.assertEquals(result["itemList"][0]["id"], song_c.id)
self.assertEquals(result["itemList"][1]["id"], song_b.id)
self.assertEquals(result["itemList"][2]["id"], song_a.id)
self.assertFalse(result["hasNextPage"])
def testCountAndPage(self):
song_a = self.addSong(
artist=self.addArtist(),
filename=__file__
)
song_b = self.addSong(
artist=self.addArtist(),
filename=__file__
)
song_c = self.addSong(
artist=self.addArtist(),
filename=__file__
)
self.addSongToQueue(song_a)
self.addSongToQueue(song_b)
self.addSongToQueue(song_c)
self.getNextSong()
self.getNextSong()
self.getNextSong()
result = simplejson.loads(
self.httpGet(
"/api/v1/history?count=1&page=1"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song_c.id)
self.assertTrue(result["hasNextPage"])
result = simplejson.loads(
self.httpGet(
"/api/v1/history?count=1&page=2"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song_b.id)
self.assertTrue(result["hasNextPage"])
result = simplejson.loads(
self.httpGet(
"/api/v1/history?count=1&page=3"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertFalse(result["hasNextPage"])
result = simplejson.loads(
self.httpGet(
"/api/v1/history/my?count=1&page=1"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song_c.id)
self.assertTrue(result["hasNextPage"])
result = simplejson.loads(
self.httpGet(
"/api/v1/history/my?count=1&page=2"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song_b.id)
self.assertTrue(result["hasNextPage"])
result = simplejson.loads(
self.httpGet(
"/api/v1/history/my?count=1&page=3"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertFalse(result["hasNextPage"])
================================================
FILE: jukebox/jukebox_core/tests/api_queue.py
================================================
# -*- coding: UTF-8 -*-
import simplejson
from jukebox.jukebox_core.tests.api import ApiTestBase
class ApiQueueTest(ApiTestBase):
def testIndexEmpty(self):
result = simplejson.loads(
self.httpGet(
"/api/v1/queue"
).content
)
self.assertEquals(len(result["itemList"]), 0)
def testAddAndIndex(self):
song = self.addSong(artist=self.addArtist())
# check that song is not in queue
result = simplejson.loads(
self.httpGet(
"/api/v1/songs"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song.id)
self.assertFalse(result["itemList"][0]["queued"])
# add to queue
response = self.httpPost(
"/api/v1/queue",
{"id": song.id}
)
content = simplejson.loads(
response.content
)
self.assertEqual(response.status_code, 201)
self.assertEqual(content["id"], song.id)
# check queue
result = simplejson.loads(
self.httpGet(
"/api/v1/queue"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song.id)
# check that song is marked as queued
result = simplejson.loads(
self.httpGet(
"/api/v1/songs"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song.id)
self.assertTrue(result["itemList"][0]["queued"])
def testDeleteAndIndex(self):
song = self.addSong(artist=self.addArtist())
# add to queue
response = self.httpPost(
"/api/v1/queue",
{"id": song.id}
)
content = simplejson.loads(
response.content
)
self.assertEqual(response.status_code, 201)
self.assertEqual(content["id"], song.id)
# check queue
result = simplejson.loads(
self.httpGet(
"/api/v1/queue"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song.id)
# remove from queue
response = self.httpDelete(
"/api/v1/queue/" + str(song.id),
)
content = simplejson.loads(
response.content
)
self.assertEqual(response.status_code, 200)
self.assertEqual(content["id"], str(song.id))
# check queue
result = simplejson.loads(
self.httpGet(
"/api/v1/queue"
).content
)
self.assertEquals(len(result["itemList"]), 0)
def addToQueue(self, song):
return self.httpPost(
"/api/v1/queue",
{"id": song.id}
)
def testIndexOrderByTitle(self):
song_a = self.addSong(artist=self.addArtist(), title="A Title")
song_b = self.addSong(artist=self.addArtist(), title="B Title")
self.addToQueue(song_a)
self.addToQueue(song_b)
result = simplejson.loads(
self.httpGet(
"/api/v1/queue?order_by=title"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertEquals(result["itemList"][1]["id"], song_b.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/queue?order_by=title&order_direction=desc"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_b.id)
self.assertEquals(result["itemList"][1]["id"], song_a.id)
def testIndexOrderByArtist(self):
song_a = self.addSong(artist=self.addArtist(name="A Name"))
song_b = self.addSong(artist=self.addArtist(name="B Name"))
self.addToQueue(song_a)
self.addToQueue(song_b)
result = simplejson.loads(
self.httpGet(
"/api/v1/queue?order_by=artist"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertEquals(result["itemList"][1]["id"], song_b.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/queue?order_by=artist&order_direction=desc"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_b.id)
self.assertEquals(result["itemList"][1]["id"], song_a.id)
def testIndexOrderByAlbum(self):
album_a = self.addAlbum(title="A Title")
album_b = self.addAlbum(title="B Title")
song_a = self.addSong(artist=self.addArtist(), album=album_a)
song_b = self.addSong(artist=self.addArtist(), album=album_b)
self.addToQueue(song_a)
self.addToQueue(song_b)
result = simplejson.loads(
self.httpGet(
"/api/v1/queue?order_by=album"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertEquals(result["itemList"][1]["id"], song_b.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/queue?order_by=album&order_direction=desc"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_b.id)
self.assertEquals(result["itemList"][1]["id"], song_a.id)
def testIndexOrderByYear(self):
song_a = self.addSong(artist=self.addArtist(), year=2000)
song_b = self.addSong(artist=self.addArtist(), year=2001)
self.addToQueue(song_a)
self.addToQueue(song_b)
result = simplejson.loads(
self.httpGet(
"/api/v1/queue?order_by=year"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertEquals(result["itemList"][1]["id"], song_b.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/queue?order_by=year&order_direction=desc"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_b.id)
self.assertEquals(result["itemList"][1]["id"], song_a.id)
def testIndexOrderByGenre(self):
song_a = self.addSong(
artist=self.addArtist(),
genre=self.addGenre(name="A Genre")
)
song_b = self.addSong(
artist=self.addArtist(),
genre=self.addGenre(name="B Genre")
)
self.addToQueue(song_a)
self.addToQueue(song_b)
result = simplejson.loads(
self.httpGet(
"/api/v1/queue?order_by=genre"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertEquals(result["itemList"][1]["id"], song_b.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/queue?order_by=genre&order_direction=desc"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_b.id)
self.assertEquals(result["itemList"][1]["id"], song_a.id)
def testIndexOrderByCreated(self):
song_a = self.addSong(artist=self.addArtist())
song_b = self.addSong(artist=self.addArtist())
self.addToQueue(song_a)
self.addToQueue(song_b)
result = simplejson.loads(
self.httpGet(
"/api/v1/queue?order_by=created"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertEquals(result["itemList"][1]["id"], song_b.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/queue?order_by=created&order_direction=desc"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_b.id)
self.assertEquals(result["itemList"][1]["id"], song_a.id)
def testCount(self):
song_a = self.addSong(artist=self.addArtist())
song_b = self.addSong(artist=self.addArtist())
song_c = self.addSong(artist=self.addArtist())
self.addToQueue(song_a)
self.addToQueue(song_b)
self.addToQueue(song_c)
result = simplejson.loads(
self.httpGet(
"/api/v1/queue?count=1"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertTrue(result["hasNextPage"])
result = simplejson.loads(
self.httpGet(
"/api/v1/queue?count=3"
).content
)
self.assertEquals(len(result["itemList"]), 3)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertEquals(result["itemList"][1]["id"], song_b.id)
self.assertEquals(result["itemList"][2]["id"], song_c.id)
self.assertFalse(result["hasNextPage"])
def testCountAndPage(self):
song_a = self.addSong(artist=self.addArtist())
song_b = self.addSong(artist=self.addArtist())
song_c = self.addSong(artist=self.addArtist())
self.addToQueue(song_a)
self.addToQueue(song_b)
self.addToQueue(song_c)
result = simplejson.loads(
self.httpGet(
"/api/v1/queue?count=1&page=1"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertTrue(result["hasNextPage"])
result = simplejson.loads(
self.httpGet(
"/api/v1/queue?count=1&page=2"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song_b.id)
self.assertTrue(result["hasNextPage"])
result = simplejson.loads(
self.httpGet(
"/api/v1/queue?count=1&page=3"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song_c.id)
self.assertFalse(result["hasNextPage"])
================================================
FILE: jukebox/jukebox_core/tests/api_songs.py
================================================
# -*- coding: UTF-8 -*-
import random, simplejson
from jukebox.jukebox_core import api
from jukebox.jukebox_core.tests.api import ApiTestBase
class ApiSongsTest(ApiTestBase):
def testIndexEmpty(self):
result = simplejson.loads(
self.httpGet(
"/api/v1/songs"
).content
)
self.assertEquals(len(result["itemList"]), 0)
def testIndex(self):
song = self.addSong(artist=self.addArtist())
result = simplejson.loads(
self.httpGet(
"/api/v1/songs"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song.id)
def testIndexWithSearchTermInTitle(self):
fixture = "thisIsATestFixtureString"
fixturePart = fixture[0:random.randint(5, len(fixture))]
song = self.addSong(artist=self.addArtist(), title=fixture)
self.addSong(artist=self.addArtist(), title="AAAAAAAAAAAAAA")
result = simplejson.loads(
self.httpGet(
"/api/v1/songs?search_term=" + fixturePart
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song.id)
def testIndexWithSearchTermInArtistName(self):
fixture = "thisIsATestFixtureString"
fixturePart = fixture[0:random.randint(5, len(fixture))]
song = self.addSong(artist=self.addArtist(name=fixture))
self.addSong(artist=self.addArtist(name="AAAAAAAAAAAAAA"))
result = simplejson.loads(
self.httpGet(
"/api/v1/songs?search_term=" + fixturePart
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song.id)
def testIndexWithSearchTermInAlbumTitle(self):
fixture = "thisIsATestFixtureString"
fixturePart = fixture[0:random.randint(5, len(fixture))]
artist = self.addArtist()
album = self.addAlbum(title=fixture)
song = self.addSong(artist=artist, album=album)
self.addSong(
artist=artist,
album=self.addAlbum(title="AAAAAAAAAAAAAA")
)
result = simplejson.loads(
self.httpGet(
"/api/v1/songs?search_term=" + fixturePart
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song.id)
def testIndexWithSearchTitle(self):
fixture = "thisIsATestFixtureString"
fixturePart = fixture[0:random.randint(5, len(fixture))]
song = self.addSong(artist=self.addArtist(), title=fixture)
self.addSong(artist=self.addArtist(), title="AAAAAAAAAAAAAA")
result = simplejson.loads(
self.httpGet(
"/api/v1/songs?search_title=" + fixturePart
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song.id)
def testIndexWithSearchArtistName(self):
fixture = "thisIsATestFixtureString"
fixturePart = fixture[0:random.randint(5, len(fixture))]
song = self.addSong(artist=self.addArtist(name=fixture))
self.addSong(artist=self.addArtist(name="AAAAAAAAAAAAAA"))
result = simplejson.loads(
self.httpGet(
"/api/v1/songs?search_artist=" + fixturePart
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song.id)
def testIndexWithSearchAlbumTitle(self):
fixture = "thisIsATestFixtureString"
fixturePart = fixture[0:random.randint(5, len(fixture))]
artist = self.addArtist()
album = self.addAlbum(title=fixture)
song = self.addSong(artist=artist, album=album)
self.addSong(
artist=artist,
album=self.addAlbum(title="AAAAAAAAAAAAAA")
)
result = simplejson.loads(
self.httpGet(
"/api/v1/songs?search_album=" + fixturePart
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song.id)
def testIndexWithFilterYear(self):
fixture = 2010
song = self.addSong(artist=self.addArtist(), year=fixture)
self.addSong(artist=self.addArtist(), year=2001)
result = simplejson.loads(
self.httpGet(
"/api/v1/songs?filter_year=" + str(fixture)
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song.id)
def testIndexWithFilterGenre(self):
genre = self.addGenre()
song = self.addSong(artist=self.addArtist(), genre=genre)
self.addSong(artist=self.addArtist(), genre=self.addGenre())
result = simplejson.loads(
self.httpGet(
"/api/v1/songs?filter_genre=" + str(genre.id)
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song.id)
def testIndexWithFilterAlbumId(self):
artist = self.addArtist()
album = self.addAlbum(title="Foo")
song = self.addSong(artist=artist, album=album)
self.addSong(artist=artist, album=self.addAlbum(title="Bar"))
result = simplejson.loads(
self.httpGet(
"/api/v1/songs?filter_album_id=" + str(album.id)
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song.id)
def testIndexWithFilterArtistId(self):
artist = self.addArtist()
song = self.addSong(artist=artist)
self.addSong(artist=self.addArtist())
result = simplejson.loads(
self.httpGet(
"/api/v1/songs?filter_artist_id=" + str(artist.id)
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song.id)
def testIndexOrderByTitle(self):
song_a = self.addSong(artist=self.addArtist(), title="A Title")
song_b = self.addSong(artist=self.addArtist(), title="B Title")
result = simplejson.loads(
self.httpGet(
"/api/v1/songs?order_by=title"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertEquals(result["itemList"][1]["id"], song_b.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/songs?order_by=title&order_direction=desc"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_b.id)
self.assertEquals(result["itemList"][1]["id"], song_a.id)
def testIndexOrderByArtist(self):
song_a = self.addSong(artist=self.addArtist(name="A Name"))
song_b = self.addSong(artist=self.addArtist(name="B Name"))
result = simplejson.loads(
self.httpGet(
"/api/v1/songs?order_by=artist"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertEquals(result["itemList"][1]["id"], song_b.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/songs?order_by=artist&order_direction=desc"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_b.id)
self.assertEquals(result["itemList"][1]["id"], song_a.id)
def testIndexOrderByAlbum(self):
artist = self.addArtist()
album_a = self.addAlbum(title="A Title")
album_b = self.addAlbum(title="B Title")
song_a = self.addSong(artist=artist, album=album_a)
song_b = self.addSong(artist=artist, album=album_b)
result = simplejson.loads(
self.httpGet(
"/api/v1/songs?order_by=album"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertEquals(result["itemList"][1]["id"], song_b.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/songs?order_by=album&order_direction=desc"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_b.id)
self.assertEquals(result["itemList"][1]["id"], song_a.id)
def testIndexOrderByYear(self):
song_a = self.addSong(artist=self.addArtist(), year=2000)
song_b = self.addSong(artist=self.addArtist(), year=2001)
result = simplejson.loads(
self.httpGet(
"/api/v1/songs?order_by=year"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertEquals(result["itemList"][1]["id"], song_b.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/songs?order_by=year&order_direction=desc"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_b.id)
self.assertEquals(result["itemList"][1]["id"], song_a.id)
def testIndexOrderByGenre(self):
song_a = self.addSong(
artist=self.addArtist(),
genre=self.addGenre(name="A Name")
)
song_b = self.addSong(
artist=self.addArtist(),
genre=self.addGenre(name="B Name")
)
result = simplejson.loads(
self.httpGet(
"/api/v1/songs?order_by=genre"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertEquals(result["itemList"][1]["id"], song_b.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/songs?order_by=genre&order_direction=desc"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_b.id)
self.assertEquals(result["itemList"][1]["id"], song_a.id)
def testIndexOrderByLength(self):
song_a = self.addSong(artist=self.addArtist(), length=100)
song_b = self.addSong(artist=self.addArtist(), length=200)
result = simplejson.loads(
self.httpGet(
"/api/v1/songs?order_by=length"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertEquals(result["itemList"][1]["id"], song_b.id)
result = simplejson.loads(
self.httpGet(
"/api/v1/songs?order_by=length&order_direction=desc"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["id"], song_b.id)
self.assertEquals(result["itemList"][1]["id"], song_a.id)
def testCount(self):
song_a = self.addSong(artist=self.addArtist())
song_b = self.addSong(artist=self.addArtist())
song_c = self.addSong(artist=self.addArtist())
result = simplejson.loads(
self.httpGet(
"/api/v1/songs?count=1"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertTrue(result["hasNextPage"])
result = simplejson.loads(
self.httpGet(
"/api/v1/songs?count=3"
).content
)
self.assertEquals(len(result["itemList"]), 3)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertEquals(result["itemList"][1]["id"], song_b.id)
self.assertEquals(result["itemList"][2]["id"], song_c.id)
self.assertFalse(result["hasNextPage"])
def testCountAndPage(self):
song_a = self.addSong(artist=self.addArtist())
song_b = self.addSong(artist=self.addArtist())
song_c = self.addSong(artist=self.addArtist())
result = simplejson.loads(
self.httpGet(
"/api/v1/songs?count=1&page=1"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song_a.id)
self.assertTrue(result["hasNextPage"])
result = simplejson.loads(
self.httpGet(
"/api/v1/songs?count=1&page=2"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song_b.id)
self.assertTrue(result["hasNextPage"])
result = simplejson.loads(
self.httpGet(
"/api/v1/songs?count=1&page=3"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["id"], song_c.id)
self.assertFalse(result["hasNextPage"])
def testGetNextSongRandom(self):
song = self.addSong(artist=self.addArtist(), filename=__file__)
songs_api = api.songs()
result = songs_api.getNextSong()
self.assertEquals(result, song)
# check if song has been added to history
history_api = api.history()
result = history_api.index()
self.assertEqual(result["itemList"][0]["id"], song.id)
def testGetNextSongFromQueue(self):
song = self.addSong(artist=self.addArtist(), filename=__file__)
# add to queue
queue_api = api.queue()
queue_api.set_user_id(self.user.id)
queue_api.add(song.id)
# get next song
songs_api = api.songs()
result = songs_api.getNextSong()
self.assertEquals(result, song)
# check if song has been added to history
history_api = api.history()
result = history_api.index()
self.assertEqual(result["itemList"][0]["id"], song.id)
# check if song has been removed from queue
result = queue_api.index()
self.assertEqual(len(result["itemList"]), 0)
================================================
FILE: jukebox/jukebox_core/tests/api_years.py
================================================
# -*- coding: UTF-8 -*-
import simplejson
from jukebox.jukebox_core.tests.api import ApiTestBase
class ApiYearsTest(ApiTestBase):
def testIndexEmpty(self):
result = simplejson.loads(
self.httpGet(
"/api/v1/years"
).content
)
self.assertEquals(len(result["itemList"]), 0)
def testIndex(self):
year = 2000
self.addSong(artist=self.addArtist(), year=year)
result = simplejson.loads(
self.httpGet(
"/api/v1/years"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["year"], year)
def testIndexOrderBy(self):
year_a = 2000
year_b = 2010
self.addSong(artist=self.addArtist(), year=year_a)
self.addSong(artist=self.addArtist(), year=year_b)
result = simplejson.loads(
self.httpGet(
"/api/v1/years?order_by=year"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["year"], year_a)
self.assertEquals(result["itemList"][1]["year"], year_b)
result = simplejson.loads(
self.httpGet(
"/api/v1/years?order_by=year&order_direction=desc"
).content
)
self.assertEquals(len(result["itemList"]), 2)
self.assertEquals(result["itemList"][0]["year"], year_b)
self.assertEquals(result["itemList"][1]["year"], year_a)
def testCount(self):
year_a = 2000
year_b = 2005
year_c = 2010
self.addSong(artist=self.addArtist(), year=year_a)
self.addSong(artist=self.addArtist(), year=year_b)
self.addSong(artist=self.addArtist(), year=year_c)
result = simplejson.loads(
self.httpGet(
"/api/v1/years?count=1"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["year"], year_a)
self.assertTrue(result["hasNextPage"])
result = simplejson.loads(
self.httpGet(
"/api/v1/years?count=3"
).content
)
self.assertEquals(len(result["itemList"]), 3)
self.assertEquals(result["itemList"][0]["year"], year_a)
self.assertEquals(result["itemList"][1]["year"], year_b)
self.assertEquals(result["itemList"][2]["year"], year_c)
self.assertFalse(result["hasNextPage"])
def testCountAndPage(self):
year_a = 2000
year_b = 2005
year_c = 2010
self.addSong(artist=self.addArtist(), year=year_a)
self.addSong(artist=self.addArtist(), year=year_b)
self.addSong(artist=self.addArtist(), year=year_c)
result = simplejson.loads(
self.httpGet(
"/api/v1/years?count=1&page=1"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["year"], year_a)
self.assertTrue(result["hasNextPage"])
result = simplejson.loads(
self.httpGet(
"/api/v1/years?count=1&page=2"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["year"], year_b)
self.assertTrue(result["hasNextPage"])
result = simplejson.loads(
self.httpGet(
"/api/v1/years?count=1&page=3"
).content
)
self.assertEquals(len(result["itemList"]), 1)
self.assertEquals(result["itemList"][0]["year"], year_c)
self.assertFalse(result["hasNextPage"])
================================================
FILE: jukebox/jukebox_core/urls.py
================================================
# -*- coding: UTF-8 -*-
from django.conf.urls import patterns, url
import views
urlpatterns = patterns("",
url(
r"^api/v1/songs$",
views.songs.as_view(),
name="jukebox_api_songs"
),
url(
r"^api/v1/songs/skip$",
views.songs_skip.as_view(),
name="jukebox_api_songs_skip"
),
url(
r"^api/v1/songs/current",
views.songs_current.as_view(),
name="jukebox_api_songs_current"
),
url(
r"^api/v1/artists$",
views.artists.as_view(),
name="jukebox_api_artists"
),
url(
r"^api/v1/albums$",
views.albums.as_view(),
name="jukebox_api_albums"
),
url(
r"^api/v1/genres$",
views.genres.as_view(),
name="jukebox_api_genres"
),
url(
r"^api/v1/years$",
views.years.as_view(),
name="jukebox_api_years"
),
url(
r"^api/v1/history$",
views.history.as_view(),
name="jukebox_api_history"
),
url(
r"^api/v1/history/my$",
views.history_my.as_view(),
name="jukebox_api_history_my"
),
url(
r"^api/v1/favourites$",
views.favourites.as_view(),
name="jukebox_api_favourites"
),
url(
r"^api/v1/favourites/(?P<song_id>[0-9]+)$",
views.favourites_item.as_view(),
name="jukebox_api_favourites_item"
),
url(
r"^api/v1/queue$",
views.queue.as_view(),
name="jukebox_api_queue"
),
url(
r"^api/v1/queue/(?P<song_id>[0-9]+)$",
views.queue_item.as_view(),
name="jukebox_api_queue_item"
),
url(
r"^api/v1/ping$",
views.ping.as_view(),
name="jukebox_api_ping"
),
)
================================================
FILE: jukebox/jukebox_core/utils.py
================================================
# -*- coding: UTF-8 -*-
from jukebox.jukebox_core.models import Artist, Album, Song, Genre
from mutagen.easyid3 import EasyID3
from mutagen.mp3 import MP3, HeaderNotFoundError
from mutagen.id3 import ID3NoHeaderError
class FileIndexer:
def index(self, filename):
# skip already indexed
if self.is_indexed(filename):
return
try:
id3 = EasyID3(filename)
tags = {
"artist": None,
"title": None,
"album": None,
"genre": None,
"date": None,
"length": None,
}
for k, v in id3.items():
tags[k] = v[0].lower()
if tags["artist"] is None or tags["title"] is None:
print "Artist or title not set in " + \
filename + " - skipping file"
return
if tags["artist"] is not None:
tags["artist"], created = Artist.objects.get_or_create(
Name=tags["artist"]
)
if tags["album"] is not None and tags["artist"] is not None:
tags["album"], created = Album.objects.get_or_create(
Title=tags["album"]
)
if tags["genre"] is not None:
tags["genre"], created = Genre.objects.get_or_create(
Name=tags["genre"]
)
if tags["date"] is not None:
try:
tags["date"] = int(tags["date"])
except ValueError:
tags["date"] = None
audio = MP3(filename)
tags["length"] = int(audio.info.length)
song = Song(
Artist=tags["artist"],
Album=tags["album"],
Genre=tags["genre"],
Title=tags["title"],
Year=tags["date"],
Length=tags["length"],
Filename=filename
)
song.save()
except HeaderNotFoundError:
print "File contains invalid header data: " + filename
except ID3NoHeaderError:
print "File does not contain an id3 header: " + filename
def delete(self, filename):
# single file
Song.objects.filter(Filename__exact=filename).delete()
# directory
Song.objects.filter(Filename__startswith=filename).delete()
def is_indexed(self, filename):
data = Song.objects.filter(Filename__exact=filename)
if not data:
return False
return True
================================================
FILE: jukebox/jukebox_core/views.py
================================================
# -*- coding: UTF-8 -*-
from django.core.urlresolvers import reverse
from django.core.exceptions import ObjectDoesNotExist
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
import api, base64
import forms
class JukeboxAPIView(APIView):
def api_set_user_id(self, request, api):
if request.user.is_authenticated():
api.set_user_id(request.user.id)
return api
class songs(JukeboxAPIView):
permissions = (IsAuthenticated, )
def get(self, request):
request.session.modified = True
page = 1
songs_api = api.songs()
songs_api = self.api_set_user_id(request, songs_api)
form = forms.SongsForm(request.GET)
if form.is_valid():
if not form.cleaned_data["search_term"] == "":
songs_api.set_search_term(
form.cleaned_data["search_term"]
)
if not form.cleaned_data["search_title"] == "":
songs_api.set_search_title(
form.cleaned_data["search_title"]
)
if not form.cleaned_data["search_artist"] == "":
songs_api.set_search_artist_name(
form.cleaned_data["search_artist"]
)
if not form.cleaned_data["search_album"] == "":
songs_api.set_search_album_title(
form.cleaned_data["search_album"]
)
if not form.cleaned_data["filter_artist_id"] is None:
songs_api.set_filter_artist_id(
form.cleaned_data["filter_artist_id"]
)
if not form.cleaned_data["filter_album_id"] is None:
songs_api.set_filter_album_id(
form.cleaned_data["filter_album_id"]
)
if not form.cleaned_data["filter_genre"] is None:
songs_api.set_filter_genre(
form.cleaned_data["filter_genre"]
)
if not form.cleaned_data["filter_year"] is None:
songs_api.set_filter_year(
form.cleaned_data["filter_year"]
)
if (not form.cleaned_data["order_by"] == "" and
not form.cleaned_data["order_direction"] == ""):
songs_api.set_order_by(
form.cleaned_data["order_by"],
form.cleaned_data["order_direction"]
)
elif not form.cleaned_data["order_by"] == "":
songs_api.set_order_by(form.cleaned_data["order_by"])
if not form.cleaned_data["count"] is None:
songs_api.set_count(form.cleaned_data["count"])
if not form.cleaned_data["page"] is None:
page = form.cleaned_data["page"]
result = songs_api.index(page)
result["form"] = form.cleaned_data
return Response(
data=result
)
class songs_current(JukeboxAPIView):
permissions = (IsAuthenticated, )
def get(self, request):
request.session.modified = True
history = api.history()
current = {}
try:
current = history.getCurrent()
except:
pass
return Response(
data=current
)
class songs_skip(JukeboxAPIView):
permissions = (IsAuthenticated, )
def get(self, request):
request.session.modified = True
songs_api = api.songs()
songs_api.skipCurrentSong()
return Response("")
class artists(JukeboxAPIView):
permissions = (IsAuthenticated, )
def get(self, request):
request.session.modified = True
page = 1
artists_api = api.artists()
form = forms.ArtistsForm(request.GET)
if form.is_valid():
if (not form.cleaned_data["order_by"] == "" and
not form.cleaned_data["order_direction"] == ""):
artists_api.set_order_by(
form.cleaned_data["order_by"],
form.cleaned_data["order_direction"]
)
elif not form.cleaned_data["order_by"] == "":
artists_api.set_order_by(form.cleaned_data["order_by"])
if not form.cleaned_data["count"] is None:
artists_api.set_count(form.cleaned_data["count"])
if not form.cleaned_data["page"] is None:
page = form.cleaned_data["page"]
return Response(
data=artists_api.index(page)
)
class albums(JukeboxAPIView):
permissions = (IsAuthenticated, )
def get(self, request):
request.session.modified = True
page = 1
albums_api = api.albums()
form = forms.AlbumsForm(request.GET)
if form.is_valid():
if (not form.cleaned_data["order_by"] == "" and
not form.cleaned_data["order_direction"] == ""):
albums_api.set_order_by(
form.cleaned_data["order_by"],
form.cleaned_data["order_direction"]
)
elif not form.cleaned_data["order_by"] == "":
albums_api.set_order_by(form.cleaned_data["order_by"])
if not form.cleaned_data["count"] is None:
albums_api.set_count(form.cleaned_data["count"])
if not form.cleaned_data["page"] is None:
page = form.cleaned_data["page"]
return Response(
data=albums_api.index(page)
)
class genres(JukeboxAPIView):
permissions = (IsAuthenticated, )
def get(self, request):
request.session.modified = True
page = 1
genres_api = api.genres()
form = forms.GenresForm(request.GET)
if form.is_valid():
if (not form.cleaned_data["order_by"] == "" and
not form.cleaned_data["order_direction"] == ""):
genres_api.set_order_by(
form.cleaned_data["order_by"],
form.cleaned_data["order_direction"]
)
elif not form.cleaned_data["order_by"] == "":
genres_api.set_order_by(form.cleaned_data["order_by"])
if not form.cleaned_data["count"] is None:
genres_api.set_count(form.cleaned_data["count"])
if not form.cleaned_data["page"] is None:
page = form.cleaned_data["page"]
return Response(
data=genres_api.index(page)
)
class years(JukeboxAPIView):
permissions = (IsAuthenticated, )
def get(self, request):
request.session.modified = True
page = 1
years_api = api.years()
form = forms.YearsForm(request.GET)
if form.is_valid():
if (not form.cleaned_data["order_by"] == "" and
not form.cleaned_data["order_direction"] == ""):
years_api.set_order_by(
form.cleaned_data["order_by"],
form.cleaned_data["order_direction"]
)
elif not form.cleaned_data["order_by"] == "":
years_api.set_order_by(form.cleaned_data["order_by"])
if not form.cleaned_data["count"] is None:
years_api.set_count(form.cleaned_data["count"])
if not form.cleaned_data["page"] is None:
page = form.cleaned_data["page"]
return Response(
data=years_api.index(page)
)
class history(JukeboxAPIView):
permissions = (IsAuthenticated, )
def get(self, request):
request.session.modified = True
page = 1
history_api = api.history()
history_api = self.api_set_user_id(request, history_api)
form = forms.HistoryForm(request.GET)
if form.is_valid():
if (not form.cleaned_data["order_by"] == "" and
not form.cleaned_data["order_direction"] == ""):
history_api.set_order_by(
form.cleaned_data["order_by"],
form.cleaned_data["order_direction"]
)
elif not form.cleaned_data["order_by"] == "":
history_api.set_order_by(form.cleaned_data["order_by"])
if not form.cleaned_data["count"] is None:
history_api.set_count(form.cleaned_data["count"])
if not form.cleaned_data["page"] is None:
page = form.cleaned_data["page"]
return Response(
data=history_api.index(page)
)
class history_my(JukeboxAPIView):
permissions = (IsAuthenticated, )
def get(self, request):
request.session.modified = True
page = 1
history_api = api.history_my()
history_api = self.api_set_user_id(request, history_api)
form = forms.HistoryForm(request.GET)
if form.is_valid():
if (not form.cleaned_data["order_by"] == "" and
not form.cleaned_data["order_direction"] == ""):
history_api.set_order_by(
form.cleaned_data["order_by"],
form.cleaned_data["order_direction"]
)
elif not form.cleaned_data["order_by"] == "":
history_api.set_order_by(form.cleaned_data["order_by"])
if not form.cleaned_data["count"] is None:
history_api.set_count(form.cleaned_data["count"])
if not form.cleaned_data["page"] is None:
page = form.cleaned_data["page"]
return Response(
data=history_api.index(page)
)
class queue(JukeboxAPIView):
permissions = (IsAuthenticated, )
form = forms.IdForm
def get(self, request):
request.session.modified = True
page = 1
queue_api = api.queue()
queue_api = self.api_set_user_id(request, queue_api)
form = forms.QueueForm(request.GET)
if form.is_valid():
if (not form.cleaned_data["order_by"] == "" and
not form.cleaned_data["order_direction"] == ""):
queue_api.set_order_by(
form.cleaned_data["order_by"],
form.cleaned_data["order_direction"]
)
elif not form.cleaned_data["order_by"] == "":
queue_api.set_order_by(form.cleaned_data["order_by"])
if not form.cleaned_data["count"] is None:
queue_api.set_count(form.cleaned_data["count"])
if not form.cleaned_data["page"] is None:
page = form.cleaned_data["page"]
result = queue_api.index(page)
for k, v in enumerate(result["itemList"]):
result["itemList"][k]["url"] = reverse(
"jukebox_api_queue_item",
kwargs={"song_id": v["id"]}
)
return Response(
data=result
)
def post(self, request):
request.session.modified = True
queue_api = api.queue()
queue_api = self.api_set_user_id(request, queue_api)
try:
song_id = queue_api.add(self.request.POST["id"])
return Response(
status=status.HTTP_201_CREATED,
data={
'id': int(self.request.POST['id'])
},
headers={"Location": reverse(
"jukebox_api_queue_item",
kwargs={"song_id": song_id}
)}
)
except ObjectDoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
except Exception, e:
print e
return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR)
class queue_item(JukeboxAPIView):
permissions = (IsAuthenticated, )
form = forms.IdForm
def get(self, request, song_id):
request.session.modified = True
queue_api = api.queue()
queue_api = self.api_set_user_id(request, queue_api)
try:
item = queue_api.get(song_id)
item["url"] = reverse(
"jukebox_api_queue_item",
kwargs={"song_id": item["id"]}
)
return Response(
data=item
)
except ObjectDoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
except Exception, e:
print e
return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR)
def delete(self, request, song_id):
request.session.modified = True
queue_api = api.queue()
queue_api = self.api_set_user_id(request, queue_api)
try:
return Response(
data=queue_api.remove(song_id)
)
except ObjectDoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
except:
return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR)
class favourites(JukeboxAPIView):
permissions = (IsAuthenticated, )
form = forms.IdForm
def get(self, request):
request.session.modified = True
page = 1
favourites_api = api.favourites()
favourites_api = self.api_set_user_id(request, favourites_api)
form = forms.FavouritesForm(request.GET)
if form.is_valid():
if (not form.cleaned_data["order_by"] == "" and
not form.cleaned_data["order_direction"] == ""):
favourites_api.set_order_by(
form.cleaned_data["order_by"],
form.cleaned_data["order_direction"]
)
elif not form.cleaned_data["order_by"] == "":
favourites_api.set_order_by(form.cleaned_data["order_by"])
if not form.cleaned_data["count"] is None:
favourites_api.set_count(form.cleaned_data["count"])
if not form.cleaned_data["page"] is None:
page = form.cleaned_data["page"]
result = favourites_api.index(page)
for k, v in enumerate(result["itemList"]):
result["itemList"][k]["url"] = reverse(
"jukebox_api_favourites_item",
kwargs={"song_id": v["id"]}
)
return Response(
data=result
)
def post(self, request):
request.session.modified = True
favourites_api = api.favourites()
favourites_api = self.api_set_user_id(request, favourites_api)
try:
song_id = favourites_api.add(self.request.POST["id"])
return Response(
status=status.HTTP_201_CREATED,
data={
'id': int(self.request.POST['id']),
},
headers={"Location": reverse(
"jukebox_api_favourites_item",
kwargs={"song_id": song_id}
)}
)
except ObjectDoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
except Exception, e:
print e
return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR)
class favourites_item(JukeboxAPIView):
permissions = (IsAuthenticated, )
form = forms.IdForm
def get(self, request, song_id):
request.session.modified = True
favourites_api = api.favourites()
favourites_api = self.api_set_user_id(request, favourites_api)
try:
item = favourites_api.get(song_id)
item["url"] = reverse(
"jukebox_api_favourites_item",
kwargs={"song_id": item["id"]}
)
return Response(
data=item
)
except ObjectDoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
except Exception, e:
print e
return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR)
def delete(self, request, song_id):
request.session.modified = True
favourites_api = api.favourites()
favourites_api = self.api_set_user_id(request, favourites_api)
try:
return Response(
data=favourites_api.remove(song_id)
)
except ObjectDoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
except Exception, e:
print e
return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR)
class ping(JukeboxAPIView):
def get(self, request):
request.session.modified = True
return Response(
data= {
"ping": True
}
)
================================================
FILE: jukebox/jukebox_web/__init__.py
================================================
================================================
FILE: jukebox/jukebox_web/locale/de/LC_MESSAGES/django.po
================================================
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-11-04 20:03+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Jens Nistler <opensource@jensnistler.de>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: pt_BR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
#: templates/index.html:5 templates/index.html.py:6 templates/login.html:5
#: templates/login.html.py:6
msgid "Democratic Jukebox - your democratic music player"
msgstr "Democratic Jukebox - Dein demokratischer Musikspieler"
#: templates/index.html:15 templates/login.html:15
msgid "Jukebox logo"
msgstr "Jukebox Logo"
#: templates/index.html:18
msgid "Account options"
msgstr "Benutzereinstellungen"
#: templates/index.html:25
msgid "Search term"
msgstr "Suchbegriff"
#: templates/index.html:28 templates/index.html.py:140
msgid "Search"
msgstr "Suchen"
#: templates/index.html:35
msgid "Now playing"
msgstr "Aktuell läuft:"
#: templates/index.html:45
msgid "Navigation"
msgstr "Navigation"
#: templates/index.html:50
msgid "Queue"
msgstr "Warteschlange"
#: templates/index.html:55
msgid "History"
msgstr "Zuletzt gespielt"
#: templates/index.html:60
msgid "My History"
msgstr "Von mir gewählt"
#: templates/index.html:65
msgid "Favourites"
msgstr "Favoriten"
#: templates/index.html:71
msgid "Music"
msgstr "Musik"
#: templates/index.html:76
msgid "Songs"
msgstr "Titel"
#: templates/index.html:81
msgid "Artists"
msgstr "Künstler"
#: templates/index.html:86
msgid "Albums"
msgstr "Alben"
#: templates/index.html:91
msgid "Genres"
msgstr "Musikrichtungen"
#: templates/index.html:96
msgid "Years"
msgstr "Jahre"
#: templates/index.html:109
msgid "Title"
msgstr "Titel"
#: templates/index.html:113
msgid "Artist"
msgstr "Künstler"
#: templates/index.html:117
msgid "Album"
msgstr "Album"
#: templates/index.html:121
msgid "Genre"
msgstr "Musikrichtung"
#: templates/index.html:123
msgid "All genres"
msgstr "Alle Musikrichtungen"
#: templates/index.html:130
msgid "Year"
msgstr "Jahr"
#: templates/index.html:132
msgid "All years"
msgstr "Alle Jahre"
#: templates/index.html:139
msgid "Reset"
msgstr "Leeren"
#: templates/index.html:146
msgid "Switch language"
msgstr "Sprache ändern"
#: templates/index.html:149
msgid "English"
msgstr "Englisch"
#: templates/index.html:152
msgid "German"
msgstr "Deutsch"
#: templates/index.html:155
msgid "French"
msgstr "Französisch"
#: templates/index.html:158
msgid "Brazilian Portuguese"
msgstr "Brasilianisches Portugiesisch"
#: templates/index.html:161
msgid "Misc"
msgstr "Verschiedenes"
#: templates/index.html:164
msgid "Logout"
msgstr "Abmelden"
#: templates/login.html:21
msgid "Login"
msgstr "Login"
#: templates/login.html:35
#, python-format
msgid "Login with your %(backend)s account"
msgstr "Melde Dich mit Deinem %(backend)s Konto an"
================================================
FILE: jukebox/jukebox_web/locale/de/LC_MESSAGES/djangojs.po
================================================
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2011-11-19 14:24+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Jens Nistler <opensource@jensnistler.de>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
#: static/js/music.js:1
msgid "function"
msgstr ""
#: static/js/music.js:105 static/js/music.js.py:106 static/js/music.js:414
#: static/js/music.js.py:436 static/js/music.js:458 static/js/music.js.py:475
msgid "Revoke vote"
msgstr "Stimme zurückziehen"
#: static/js/music.js:122 static/js/music.js.py:123 static/js/music.js:439
#: static/js/music.js.py:461 static/js/music.js:478
msgid "Vote to play"
msgstr "Zum Abspielen wählen"
#: static/js/music.js:140 static/js/music.js.py:141 static/js/music.js:417
msgid "Support vote"
msgstr "Wahl unterstützen"
#: static/js/music.js:170 static/js/music.js.py:171 static/js/music.js:420
#: static/js/music.js.py:442 static/js/music.js:463 static/js/music.js.py:481
msgid "Remove from favourites"
msgstr "Aus Favoriten entfernen"
#: static/js/music.js:186 static/js/music.js.py:187 static/js/music.js:423
#: static/js/music.js.py:445 static/js/music.js:484
msgid "Add to favourites"
msgstr "Zu Favoriten hinzufügen"
#: static/js/music.js:286
msgid "No data found"
msgstr "Keine Daten gefunden"
#: static/js/music.js:351 static/js/music.js.py:359 static/js/music.js:367
#: static/js/music.js.py:375 static/js/music.js:388
msgid "Title"
msgstr "Titel"
#: static/js/music.js:352 static/js/music.js.py:360 static/js/music.js:368
#: static/js/music.js.py:376 static/js/music.js:389
msgid "Artist"
msgstr "Künstler"
#: static/js/music.js:353 static/js/music.js.py:361 static/js/music.js:369
#: static/js/music.js.py:377
msgid "Album"
msgstr "Album"
#: static/js/music.js:354 static/js/music.js.py:362
msgid "Votes"
msgstr "Stimmen"
#: static/js/music.js:355
msgid "First voted"
msgstr "Erste Stimme erhalten"
#: static/js/music.js:363 static/js/music.js.py:371
msgid "Date added"
msgstr "Hinzugefügt"
#: static/js/music.js:370 static/js/music.js.py:378
msgid "Genre"
msgstr "Musikrichtung"
#: static/js/music.js:379 static/js/music.js.py:397
msgid "Year"
msgstr "Jahr"
#: static/js/music.js:380
msgid "Length"
msgstr "Dauer"
#: static/js/music.js:384 static/js/music.js.py:393
msgid "Name"
msgstr "Name"
#: static/js/music.js:451
msgid "Autoplay"
msgstr "automatisch"
================================================
FILE: jukebox/jukebox_web/locale/en/LC_MESSAGES/django.po
================================================
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-11-04 20:03+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Jens Nistler <opensource@jensnistler.de>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: templates/index.html:5 templates/index.html.py:6 templates/login.html:5
#: templates/login.html.py:6
msgid "Democratic Jukebox - your democratic music player"
msgstr "Democratic Jukebox - your democratic music player"
#: templates/index.html:15 templates/login.html:15
msgid "Jukebox logo"
msgstr "Jukebox logo"
#: templates/index.html:18
msgid "Account options"
msgstr "Account options"
#: templates/index.html:25
msgid "Search term"
msgstr "Search term"
#: templates/index.html:28 templates/index.html.py:140
msgid "Search"
msgstr "Search"
#: templates/index.html:35
msgid "Now playing"
msgstr "Now playing:"
#: templates/index.html:45
msgid "Navigation"
msgstr "Navigation"
#: templates/index.html:50
msgid "Queue"
msgstr "Queue"
#: templates/index.html:55
msgid "History"
msgstr "History"
#: templates/index.html:60
#, fuzzy
msgid "My History"
msgstr "My History"
#: templates/index.html:65
msgid "Favourites"
msgstr "Favourites"
#: templates/index.html:71
msgid "Music"
msgstr "Music"
#: templates/index.html:76
msgid "Songs"
msgstr "Songs"
#: templates/index.html:81
msgid "Artists"
msgstr "Artists"
#: templates/index.html:86
msgid "Albums"
msgstr "Albums"
#: templates/index.html:91
msgid "Genres"
msgstr "Genres"
#: templates/index.html:96
msgid "Years"
msgstr "Years"
#: templates/index.html:109
msgid "Title"
msgstr "Title"
#: templates/index.html:113
msgid "Artist"
msgstr "Artist"
#: templates/index.html:117
msgid "Album"
msgstr "Album"
#: templates/index.html:121
msgid "Genre"
msgstr "Genre"
#: templates/index.html:123
msgid "All genres"
msgstr "All genres"
#: templates/index.html:130
msgid "Year"
msgstr "Year"
#: templates/index.html:132
msgid "All years"
msgstr "All years"
#: templates/index.html:139
msgid "Reset"
msgstr "Reset"
#: templates/index.html:146
msgid "Switch language"
msgstr "Switch language"
#: templates/index.html:149
msgid "English"
msgstr "English"
#: templates/index.html:152
msgid "German"
msgstr "German"
#: templates/index.html:155
msgid "French"
msgstr ""
#: templates/index.html:158
msgid "Brazilian Portuguese"
msgstr "Brazilian Portuguese"
#: templates/index.html:161
msgid "Misc"
msgstr "Misc"
#: templates/index.html:164
msgid "Logout"
msgstr "Logout"
#: templates/login.html:21
msgid "Login"
msgstr "Login"
#: templates/login.html:35
#, python-format
msgid "Login with your %(backend)s account"
msgstr "Login with your %(backend)s account"
================================================
FILE: jukebox/jukebox_web/locale/en/LC_MESSAGES/djangojs.po
================================================
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2011-11-19 14:24+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Jens Nistler <opensource@jensnistler.de>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: static/js/music.js:1
msgid "function"
msgstr ""
#: static/js/music.js:105 static/js/music.js.py:106 static/js/music.js:414
#: static/js/music.js.py:436 static/js/music.js:458 static/js/music.js.py:475
msgid "Revoke vote"
msgstr "Revoke vote"
#: static/js/music.js:122 static/js/music.js.py:123 static/js/music.js:439
#: static/js/music.js.py:461 static/js/music.js:478
msgid "Vote to play"
msgstr "Vote to play"
#: static/js/music.js:140 static/js/music.js.py:141 static/js/music.js:417
msgid "Support vote"
msgstr "Support vote"
#: static/js/music.js:170 static/js/music.js.py:171 static/js/music.js:420
#: static/js/music.js.py:442 static/js/music.js:463 static/js/music.js.py:481
msgid "Remove from favourites"
msgstr "Remove from favourites"
#: static/js/music.js:186 static/js/music.js.py:187 static/js/music.js:423
#: static/js/music.js.py:445 static/js/music.js:484
msgid "Add to favourites"
msgstr "Add to favourites"
#: static/js/music.js:286
msgid "No data found"
msgstr "No data found"
#: static/js/music.js:351 static/js/music.js.py:359 static/js/music.js:367
#: static/js/music.js.py:375 static/js/music.js:388
msgid "Title"
msgstr "Title"
#: static/js/music.js:352 static/js/music.js.py:360 static/js/music.js:368
#: static/js/music.js.py:376 static/js/music.js:389
msgid "Artist"
msgstr "Artist"
#: static/js/music.js:353 static/js/music.js.py:361 static/js/music.js:369
#: static/js/music.js.py:377
msgid "Album"
msgstr "Album"
#: static/js/music.js:354 static/js/music.js.py:362
msgid "Votes"
msgstr "Votes"
#: static/js/music.js:355
msgid "First voted"
msgstr "First voted"
#: static/js/music.js:363 static/js/music.js.py:371
msgid "Date added"
msgstr "Date added"
#: static/js/music.js:370 static/js/music.js.py:378
msgid "Genre"
msgstr "Genre"
#: static/js/music.js:379 static/js/music.js.py:397
msgid "Year"
msgstr "Year"
#: static/js/music.js:380
msgid "Length"
msgstr "Length"
#: static/js/music.js:384 static/js/music.js.py:393
msgid "Name"
msgstr "Name"
#: static/js/music.js:451
msgid "Autoplay"
msgstr "Autoplay"
================================================
FILE: jukebox/jukebox_web/locale/fr/LC_MESSAGES/django.po
================================================
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-11-04 20:03+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1)\n"
#: templates/index.html:5 templates/index.html.py:6 templates/login.html:5
#: templates/login.html.py:6
msgid "Democratic Jukebox - your democratic music player"
msgstr ""
#: templates/index.html:15 templates/login.html:15
msgid "Jukebox logo"
msgstr ""
#: templates/index.html:18
msgid "Account options"
msgstr "Options du compte"
#: templates/index.html:25
msgid "Search term"
msgstr "Mots recherchés"
#: templates/index.html:28 templates/index.html.py:140
msgid "Search"
msgstr "Recherche"
#: templates/index.html:35
msgid "Now playing"
msgstr "Lecture en cours"
#: templates/index.html:45
msgid "Navigation"
msgstr ""
#: templates/index.html:50
msgid "Queue"
msgstr "File"
#: templates/index.html:55
msgid "History"
msgstr "Historique"
#: templates/index.html:60
msgid "My History"
msgstr "Mon historique"
#: templates/index.html:65
msgid "Favourites"
msgstr "Favoris"
#: templates/index.html:71
msgid "Music"
msgstr "Musique"
#: templates/index.html:76
msgid "Songs"
msgstr "Chansons"
#: templates/index.html:81
msgid "Artists"
msgstr "Artistes"
#: templates/index.html:86
msgid "Albums"
msgstr ""
#: templates/index.html:91
msgid "Genres"
msgstr ""
#: templates/index.html:96
msgid "Years"
msgstr "Ans"
#: templates/index.html:109
msgid "Title"
msgstr "Titre"
#: templates/index.html:113
msgid "Artist"
msgstr "Artiste"
#: templates/index.html:117
msgid "Album"
msgstr ""
#: templates/index.html:121
msgid "Genre"
msgstr ""
#: templates/index.html:123
msgid "All genres"
msgstr "Tous les genres"
#: templates/index.html:130
msgid "Year"
msgstr "Année"
#: templates/index.html:132
msgid "All years"
msgstr "Tous les ans"
#: templates/index.html:139
msgid "Reset"
msgstr "Réinitialiser"
#: templates/index.html:146
msgid "Switch language"
msgstr "Changer de langue"
#: templates/index.html:149
msgid "English"
msgstr "Anglais"
#: templates/index.html:152
msgid "German"
msgstr "Allemand"
#: templates/index.html:155
msgid "French"
msgstr "Français"
#: templates/index.html:158
msgid "Brazilian Portuguese"
msgstr "Brésilien Portugais"
#: templates/index.html:161
msgid "Misc"
msgstr "Divers"
#: templates/index.html:164
msgid "Logout"
msgstr "Déconnexion"
#: templates/login.html:21
msgid "Login"
msgstr "Connexion"
#: templates/login.html:35
#, python-format
msgid "Login with your %(backend)s account"
msgstr "Connectez-vous avec votre compte %(backend)s"
================================================
FILE: jukebox/jukebox_web/locale/fr/LC_MESSAGES/djangojs.po
================================================
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2011-11-19 14:24+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Jens Nistler <opensource@jensnistler.de>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: static/js/music.js:1
msgid "function"
msgstr "fontion"
#: static/js/music.js:105 static/js/music.js.py:106 static/js/music.js:414
#: static/js/music.js.py:436 static/js/music.js:458 static/js/music.js.py:475
msgid "Revoke vote"
msgstr "Retirer le vote"
#: static/js/music.js:122 static/js/music.js.py:123 static/js/music.js:439
#: static/js/music.js.py:461 static/js/music.js:478
msgid "Vote to play"
msgstr "Voter à jouer"
#: static/js/music.js:140 static/js/music.js.py:141 static/js/music.js:417
msgid "Support vote"
msgstr "Supporter le vote"
#: static/js/music.js:170 static/js/music.js.py:171 static/js/music.js:420
#: static/js/music.js.py:442 static/js/music.js:463 static/js/music.js.py:481
msgid "Remove from favourites"
msgstr "Retirer des favoris"
#: static/js/music.js:186 static/js/music.js.py:187 static/js/music.js:423
#: static/js/music.js.py:445 static/js/music.js:484
msgid "Add to favourites"
msgstr "Ajouter aux favoris"
#: static/js/music.js:286
msgid "No data found"
msgstr "Aucune donnée trouvée"
#: static/js/music.js:351 static/js/music.js.py:359 static/js/music.js:367
#: static/js/music.js.py:375 static/js/music.js:388
msgid "Title"
msgstr "Titre"
#: static/js/music.js:352 static/js/music.js.py:360 static/js/music.js:368
#: static/js/music.js.py:376 static/js/music.js:389
msgid "Artist"
msgstr "Artiste"
#: static/js/music.js:353 static/js/music.js.py:361 static/js/music.js:369
#: static/js/music.js.py:377
msgid "Album"
msgstr "Album"
#: static/js/music.js:354 static/js/music.js.py:362
msgid "Votes"
msgstr "Votes"
#: static/js/music.js:355
msgid "First voted"
msgstr "Première voté"
#: static/js/music.js:363 static/js/music.js.py:371
msgid "Date added"
msgstr "Date d'ajout"
#: static/js/music.js:370 static/js/music.js.py:378
msgid "Genre"
msgstr "Genre"
#: static/js/music.js:379 static/js/music.js.py:397
msgid "Year"
msgstr "An"
#: static/js/music.js:380
msgid "Length"
msgstr "Durée"
#: static/js/music.js:384 static/js/music.js.py:393
msgid "Name"
msgstr "Nom"
#: static/js/music.js:451
msgid "Autoplay"
msgstr "Lecture automatique"
================================================
FILE: jukebox/jukebox_web/locale/pt_BR/LC_MESSAGES/django.po
================================================
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-11-04 20:03+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: templates/index.html:5 templates/index.html.py:6 templates/login.html:5
#: templates/login.html.py:6
msgid "Democratic Jukebox - your democratic music player"
msgstr "Democratic Jukebox - o seu player de música democrático"
#: templates/index.html:15 templates/login.html:15
msgid "Jukebox logo"
msgstr "logo do Jukebox"
#: templates/index.html:18
msgid "Account options"
msgstr "Opções da Conta"
#: templates/index.html:25
msgid "Search term"
msgstr "Buscar palavra"
#: templates/index.html:28 templates/index.html.py:140
msgid "Search"
msgstr "Buscar"
#: templates/index.html:35
msgid "Now playing"
msgstr "Em execução"
#: templates/index.html:45
msgid "Navigation"
msgstr "Navegação"
#: templates/index.html:50
msgid "Queue"
msgstr "Fila"
#: templates/index.html:55
msgid "History"
msgstr "Histórico"
#: templates/index.html:60
msgid "My History"
msgstr "Meu Histórico"
#: templates/index.html:65
msgid "Favourites"
msgstr "Favoritos"
#: templates/index.html:71
msgid "Music"
msgstr "Música"
#: templates/index.html:76
msgid "Songs"
msgstr "Músicas"
#: templates/index.html:81
msgid "Artists"
msgstr "Artistas"
#: templates/index.html:86
msgid "Albums"
msgstr "Albuns"
#: templates/index.html:91
msgid "Genres"
msgstr "Gêneros"
#: templates/index.html:96
msgid "Years"
msgstr "Anos"
#: templates/index.html:109
msgid "Title"
msgstr "Título"
#: templates/index.html:113
msgid "Artist"
msgstr "Artista"
#: templates/index.html:117
msgid "Album"
msgstr "Album"
#: templates/index.html:121
msgid "Genre"
msgstr "Gênero"
#: templates/index.html:123
msgid "All genres"
msgstr "Todos os gêneros"
#: templates/index.html:130
msgid "Year"
msgstr "Anos"
#: templates/index.html:132
msgid "All years"
msgstr "Todos os anos"
#: templates/index.html:139
msgid "Reset"
msgstr "Limpar"
#: templates/index.html:146
msgid "Switch language"
msgstr "Alterar lingua"
#: templates/index.html:149
msgid "English"
msgstr "Inglês"
#: templates/index.html:152
msgid "German"
msgstr "Alemão"
#: templates/index.html:155
msgid "French"
msgstr "Francês"
#: templates/index.html:158
msgid "Brazilian Portuguese"
msgstr "Português do Brasil"
#: templates/index.html:161
msgid "Misc"
msgstr "Outros"
#: templates/index.html:164
msgid "Logout"
msgstr "Sair"
#: templates/login.html:21
msgid "Login"
msgstr "Acessar"
#: templates/login.html:35
#, python-format
msgid "Login with your %(backend)s account"
msgstr "Acessar com sua conta do %(backend)s"
================================================
FILE: jukebox/jukebox_web/locale/pt_BR/LC_MESSAGES/djangojs.po
================================================
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-12-19 23:24-0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: static/js/music.js:236 static/js/music.js.c:237 static/js/music.js.c:646
#: static/js/music.js:707 static/js/music.js.c:767 static/js/music.js.c:813
msgid "Revoke vote"
msgstr "Revogar Voto"
#: static/js/music.js:252 static/js/music.js.c:253 static/js/music.js.c:710
#: static/js/music.js:770 static/js/music.js.c:816
msgid "Vote to play"
msgstr "Vote para tocar"
#: static/js/music.js:269 static/js/music.js.c:270 static/js/music.js.c:649
msgid "Support vote"
msgstr "Voto de suporte"
#: static/js/music.js:297 static/js/music.js.c:298 static/js/music.js.c:652
#: static/js/music.js:713 static/js/music.js.c:772 static/js/music.js.c:819
msgid "Remove from favourites"
msgstr "Remover dos favoritos"
#: static/js/music.js:312 static/js/music.js.c:313 static/js/music.js.c:655
#: static/js/music.js:716 static/js/music.js.c:822
msgid "Add to favourites"
msgstr "Adicionar aos favoritos"
#: static/js/music.js:466
msgid "No data found"
msgstr "Nenhuma informação encontrada"
#: static/js/music.js:572 static/js/music.js.c:581 static/js/music.js.c:590
#: static/js/music.js:599 static/js/music.js.c:608 static/js/music.js.c:621
msgid "Title"
msgstr "Título"
#: static/js/music.js:573 static/js/music.js.c:582 static/js/music.js.c:591
#: static/js/music.js:600 static/js/music.js.c:609
msgid "Artist"
msgstr "Artista"
#: static/js/music.js:574 static/js/music.js.c:583 static/js/music.js.c:592
#: static/js/music.js:601 static/js/music.js.c:610
msgid "Album"
msgstr "Album"
#: static/js/music.js:575 static/js/music.js.c:584 static/js/music.js.c:593
msgid "Votes"
msgstr "Votos"
#: static/js/music.js:576
msgid "First voted"
msgstr "Primeiro voto"
#: static/js/music.js:585 static/js/music.js.c:594 static/js/music.js.c:603
msgid "Date added"
msgstr "Data de adição"
#: static/js/music.js:602 static/js/music.js.c:611
msgid "Genre"
msgstr "Gênero"
#: static/js/music.js:612 static/js/music.js.c:629
msgid "Year"
msgstr "Ano"
#: static/js/music.js:613
msgid "Length"
msgstr "Tamanho"
#: static/js/music.js:617 static/js/music.js.c:625
msgid "Name"
msgstr "Nome"
#: static/js/music.js:696 static/js/music.js.c:757
msgid "Autoplay"
msgstr "Tocar automaticamente"
===================================
gitextract_yt7p7x97/ ├── .gitignore ├── CHANGES.txt ├── LICENSE.rst ├── MANIFEST.in ├── README.rst ├── bin/ │ └── jukebox ├── jukebox/ │ ├── __init__.py │ ├── jukebox.wsgi │ ├── jukebox_core/ │ │ ├── __init__.py │ │ ├── admin.py │ │ ├── api.py │ │ ├── docs/ │ │ │ └── API.rst │ │ ├── forms.py │ │ ├── management/ │ │ │ ├── __init__.py │ │ │ └── commands/ │ │ │ ├── __init__.py │ │ │ ├── jukebox_index.py │ │ │ └── jukebox_setup.py │ │ ├── migrations/ │ │ │ ├── 0001_initial.py │ │ │ ├── 0002_auto__del_field_album_Artist.py │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── tests/ │ │ │ ├── __init__.py │ │ │ ├── api.py │ │ │ ├── api_albums.py │ │ │ ├── api_artists.py │ │ │ ├── api_favourites.py │ │ │ ├── api_genres.py │ │ │ ├── api_history.py │ │ │ ├── api_queue.py │ │ │ ├── api_songs.py │ │ │ └── api_years.py │ │ ├── urls.py │ │ ├── utils.py │ │ └── views.py │ ├── jukebox_web/ │ │ ├── __init__.py │ │ ├── locale/ │ │ │ ├── de/ │ │ │ │ └── LC_MESSAGES/ │ │ │ │ ├── django.mo │ │ │ │ ├── django.po │ │ │ │ ├── djangojs.mo │ │ │ │ └── djangojs.po │ │ │ ├── en/ │ │ │ │ └── LC_MESSAGES/ │ │ │ │ ├── django.mo │ │ │ │ ├── django.po │ │ │ │ ├── djangojs.mo │ │ │ │ └── djangojs.po │ │ │ ├── fr/ │ │ │ │ └── LC_MESSAGES/ │ │ │ │ ├── django.mo │ │ │ │ ├── django.po │ │ │ │ ├── djangojs.mo │ │ │ │ └── djangojs.po │ │ │ └── pt_BR/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── django.mo │ │ │ ├── django.po │ │ │ ├── djangojs.mo │ │ │ └── djangojs.po │ │ ├── static/ │ │ │ ├── css/ │ │ │ │ └── music.css │ │ │ └── js/ │ │ │ └── music.js │ │ ├── templates/ │ │ │ ├── index.html │ │ │ └── login.html │ │ ├── urls.py │ │ └── views.py │ ├── manage.py │ ├── settings.py │ ├── settings_local.example.py │ └── urls.py ├── requirements.txt └── setup.py
SYMBOL INDEX (244 symbols across 20 files)
FILE: jukebox/jukebox_core/admin.py
class ArtistAdmin (line 7) | class ArtistAdmin(admin.ModelAdmin):
class GenreAdmin (line 12) | class GenreAdmin(admin.ModelAdmin):
class AlbumAdmin (line 16) | class AlbumAdmin(admin.ModelAdmin):
class SongAdmin (line 21) | class SongAdmin(admin.ModelAdmin):
class QueueAdmin (line 26) | class QueueAdmin(admin.ModelAdmin):
class HistoryAdmin (line 30) | class HistoryAdmin(admin.ModelAdmin):
class FavouriteAdmin (line 34) | class FavouriteAdmin(admin.ModelAdmin):
FILE: jukebox/jukebox_core/api.py
class api_base (line 15) | class api_base:
method set_count (line 32) | def set_count(self, count):
method set_user_id (line 38) | def set_user_id(self, user_id):
method set_search_term (line 41) | def set_search_term(self, term):
method parseSearchString (line 70) | def parseSearchString(self, keywords, term):
method set_search_title (line 118) | def set_search_title(self, term):
method set_search_artist_name (line 121) | def set_search_artist_name(self, term):
method set_search_album_title (line 124) | def set_search_album_title(self, term):
method set_filter_year (line 127) | def set_filter_year(self, term):
method set_filter_genre (line 130) | def set_filter_genre(self, term):
method set_filter_album_id (line 133) | def set_filter_album_id(self, term):
method set_filter_artist_id (line 136) | def set_filter_artist_id(self, term):
method set_order_by (line 139) | def set_order_by(self, field, direction="asc"):
method get_default_result (line 147) | def get_default_result(self, result_type, page):
method result_add_queue_and_favourite (line 185) | def result_add_queue_and_favourite(self, song, dataset):
method source_set_order (line 204) | def source_set_order(self, object_list):
method result_set_order (line 220) | def result_set_order(self, result):
class songs (line 237) | class songs(api_base):
method index (line 250) | def index(self, page=1):
method getNextSong (line 350) | def getNextSong(self):
method getRandomSongByPreferences (line 381) | def getRandomSongByPreferences(self):
method addToHistory (line 437) | def addToHistory(self, song_instance, user_list):
method skipCurrentSong (line 447) | def skipCurrentSong(self):
class history (line 455) | class history(api_base):
method index (line 468) | def index(self, page=1):
method build_result (line 475) | def build_result(self, object_list, page):
method getCurrent (line 539) | def getCurrent(self):
class history_my (line 583) | class history_my(history):
method index (line 596) | def index(self, page=1):
class queue (line 606) | class queue(api_base):
method index (line 621) | def index(self, page=1):
method get (line 644) | def get(self, song_id):
method add (line 693) | def add(self, song_id):
method remove (line 708) | def remove(self, song_id):
class favourites (line 724) | class favourites(api_base):
method index (line 737) | def index(self, page=1):
method get (line 758) | def get(self, song_id):
method add (line 801) | def add(self, song_id):
method remove (line 813) | def remove(self, song_id):
class artists (line 827) | class artists(api_base):
method index (line 835) | def index(self, page=1):
class albums (line 862) | class albums(api_base):
method index (line 870) | def index(self, page=1):
class genres (line 897) | class genres(api_base):
method index (line 905) | def index(self, page=1):
class years (line 932) | class years(api_base):
method index (line 940) | def index(self, page=1):
class players (line 967) | class players(api_base):
method add (line 968) | def add(self, pid):
method remove (line 976) | def remove(self, pid):
FILE: jukebox/jukebox_core/forms.py
class IdForm (line 6) | class IdForm(forms.Form):
class SongsForm (line 12) | class SongsForm(forms.Form):
class ArtistsForm (line 58) | class ArtistsForm(forms.Form):
class AlbumsForm (line 78) | class AlbumsForm(forms.Form):
class GenresForm (line 98) | class GenresForm(forms.Form):
class YearsForm (line 118) | class YearsForm(forms.Form):
class HistoryForm (line 138) | class HistoryForm(forms.Form):
class FavouritesForm (line 158) | class FavouritesForm(forms.Form):
class QueueForm (line 178) | class QueueForm(forms.Form):
FILE: jukebox/jukebox_core/management/commands/jukebox_index.py
class Command (line 8) | class Command(BaseCommand):
method handle (line 14) | def handle(self, *args, **options):
method index (line 27) | def index(self, path, verbosity):
FILE: jukebox/jukebox_core/management/commands/jukebox_setup.py
class Command (line 6) | class Command(BaseCommand):
method handle (line 7) | def handle(self, *args, **options):
method setAuthentication (line 27) | def setAuthentication(self):
method readAppData (line 63) | def readAppData(self, name):
method setup (line 91) | def setup(self, admin_user, admin_email, authentication):
FILE: jukebox/jukebox_core/migrations/0001_initial.py
class Migration (line 8) | class Migration(SchemaMigration):
method forwards (line 10) | def forwards(self, orm):
method backwards (line 98) | def backwards(self, orm):
FILE: jukebox/jukebox_core/migrations/0002_auto__del_field_album_Artist.py
class Migration (line 8) | class Migration(SchemaMigration):
method forwards (line 10) | def forwards(self, orm):
method backwards (line 15) | def backwards(self, orm):
FILE: jukebox/jukebox_core/models.py
class Artist (line 8) | class Artist(models.Model):
class Meta (line 9) | class Meta:
method __unicode__ (line 12) | def __unicode__(self):
class Genre (line 18) | class Genre(models.Model):
class Meta (line 19) | class Meta:
method __unicode__ (line 22) | def __unicode__(self):
class Album (line 28) | class Album(models.Model):
class Meta (line 29) | class Meta:
method __unicode__ (line 32) | def __unicode__(self):
class Song (line 38) | class Song(models.Model):
class Meta (line 39) | class Meta:
method __unicode__ (line 42) | def __unicode__(self):
class Queue (line 54) | class Queue(models.Model):
class Favourite (line 60) | class Favourite(models.Model):
class Meta (line 61) | class Meta:
class History (line 70) | class History(models.Model):
class Meta (line 71) | class Meta:
class Player (line 79) | class Player(models.Model):
class QueueFeed (line 83) | class QueueFeed(Feed):
method items (line 88) | def items(self):
method item_title (line 91) | def item_title(self, item):
method item_description (line 95) | def item_description(self, item):
method item_link (line 101) | def item_link(self, item):
FILE: jukebox/jukebox_core/tests/api.py
class ApiTestBase (line 10) | class ApiTestBase(TestCase):
method setUp (line 17) | def setUp(self):
method httpGet (line 23) | def httpGet(self, url, params={}, user=None):
method httpPost (line 27) | def httpPost(self, url, params={}, user=None):
method httpDelete (line 31) | def httpDelete(self, url, params={}, user=None):
method getAuth (line 35) | def getAuth(self, user=None):
method addArtist (line 46) | def addArtist(self, name="TestArist"):
method addAlbum (line 53) | def addAlbum(self, title="TestTitle"):
method addGenre (line 60) | def addGenre(self, name="TestGenre"):
method addSong (line 67) | def addSong(
method addUser (line 90) | def addUser(self, username, email, password):
FILE: jukebox/jukebox_core/tests/api_albums.py
class ApiAlbumsTest (line 7) | class ApiAlbumsTest(ApiTestBase):
method testIndexEmpty (line 8) | def testIndexEmpty(self):
method testIndex (line 17) | def testIndex(self):
method testIndexOrderByAlbum (line 29) | def testIndexOrderByAlbum(self):
method testCount (line 53) | def testCount(self):
method testCountAndPage (line 78) | def testCountAndPage(self):
FILE: jukebox/jukebox_core/tests/api_artists.py
class ApiArtistsTest (line 7) | class ApiArtistsTest(ApiTestBase):
method testIndexEmpty (line 8) | def testIndexEmpty(self):
method testIndex (line 17) | def testIndex(self):
method testIndexOrderBy (line 29) | def testIndexOrderBy(self):
method testCount (line 53) | def testCount(self):
method testCountAndPage (line 78) | def testCountAndPage(self):
FILE: jukebox/jukebox_core/tests/api_favourites.py
class ApiFavouritesTest (line 8) | class ApiFavouritesTest(ApiTestBase):
method testIndexEmpty (line 9) | def testIndexEmpty(self):
method testAddAndIndex (line 17) | def testAddAndIndex(self):
method testDeleteAndIndex (line 85) | def testDeleteAndIndex(self):
method addFavourite (line 126) | def addFavourite(self, song):
method testIndexOrderByTitle (line 132) | def testIndexOrderByTitle(self):
method testIndexOrderByArtist (line 158) | def testIndexOrderByArtist(self):
method testIndexOrderByAlbum (line 184) | def testIndexOrderByAlbum(self):
method testIndexOrderByYear (line 212) | def testIndexOrderByYear(self):
method testIndexOrderByGenre (line 238) | def testIndexOrderByGenre(self):
method testIndexOrderByCreated (line 270) | def testIndexOrderByCreated(self):
method testCount (line 296) | def testCount(self):
method testCountAndPage (line 324) | def testCountAndPage(self):
FILE: jukebox/jukebox_core/tests/api_genres.py
class ApiGenresTest (line 7) | class ApiGenresTest(ApiTestBase):
method testIndexEmpty (line 8) | def testIndexEmpty(self):
method testIndex (line 17) | def testIndex(self):
method testIndexOrderBy (line 29) | def testIndexOrderBy(self):
method testCount (line 53) | def testCount(self):
method testCountAndPage (line 78) | def testCountAndPage(self):
FILE: jukebox/jukebox_core/tests/api_history.py
class ApiHistoryTest (line 9) | class ApiHistoryTest(ApiTestBase):
method testIndexEmpty (line 10) | def testIndexEmpty(self):
method addSongToQueue (line 18) | def addSongToQueue(self, song, user=None):
method getNextSong (line 28) | def getNextSong(self):
method testAddAndIndex (line 32) | def testAddAndIndex(self):
method testAddAndIndexMy (line 56) | def testAddAndIndexMy(self):
method testIndexOrderByTitle (line 115) | def testIndexOrderByTitle(self):
method testIndexOrderByArtist (line 163) | def testIndexOrderByArtist(self):
method testIndexOrderByAlbum (line 211) | def testIndexOrderByAlbum(self):
method testIndexOrderByYear (line 264) | def testIndexOrderByYear(self):
method testIndexOrderByGenre (line 312) | def testIndexOrderByGenre(self):
method testIndexOrderByCreated (line 364) | def testIndexOrderByCreated(self):
method testCount (line 414) | def testCount(self):
method testCountAndPage (line 474) | def testCountAndPage(self):
FILE: jukebox/jukebox_core/tests/api_queue.py
class ApiQueueTest (line 6) | class ApiQueueTest(ApiTestBase):
method testIndexEmpty (line 7) | def testIndexEmpty(self):
method testAddAndIndex (line 15) | def testAddAndIndex(self):
method testDeleteAndIndex (line 58) | def testDeleteAndIndex(self):
method addToQueue (line 99) | def addToQueue(self, song):
method testIndexOrderByTitle (line 105) | def testIndexOrderByTitle(self):
method testIndexOrderByArtist (line 131) | def testIndexOrderByArtist(self):
method testIndexOrderByAlbum (line 157) | def testIndexOrderByAlbum(self):
method testIndexOrderByYear (line 185) | def testIndexOrderByYear(self):
method testIndexOrderByGenre (line 211) | def testIndexOrderByGenre(self):
method testIndexOrderByCreated (line 243) | def testIndexOrderByCreated(self):
method testCount (line 269) | def testCount(self):
method testCountAndPage (line 297) | def testCountAndPage(self):
FILE: jukebox/jukebox_core/tests/api_songs.py
class ApiSongsTest (line 8) | class ApiSongsTest(ApiTestBase):
method testIndexEmpty (line 9) | def testIndexEmpty(self):
method testIndex (line 18) | def testIndex(self):
method testIndexWithSearchTermInTitle (line 30) | def testIndexWithSearchTermInTitle(self):
method testIndexWithSearchTermInArtistName (line 46) | def testIndexWithSearchTermInArtistName(self):
method testIndexWithSearchTermInAlbumTitle (line 62) | def testIndexWithSearchTermInAlbumTitle(self):
method testIndexWithSearchTitle (line 83) | def testIndexWithSearchTitle(self):
method testIndexWithSearchArtistName (line 99) | def testIndexWithSearchArtistName(self):
method testIndexWithSearchAlbumTitle (line 115) | def testIndexWithSearchAlbumTitle(self):
method testIndexWithFilterYear (line 136) | def testIndexWithFilterYear(self):
method testIndexWithFilterGenre (line 150) | def testIndexWithFilterGenre(self):
method testIndexWithFilterAlbumId (line 164) | def testIndexWithFilterAlbumId(self):
method testIndexWithFilterArtistId (line 179) | def testIndexWithFilterArtistId(self):
method testIndexOrderByTitle (line 193) | def testIndexOrderByTitle(self):
method testIndexOrderByArtist (line 217) | def testIndexOrderByArtist(self):
method testIndexOrderByAlbum (line 241) | def testIndexOrderByAlbum(self):
method testIndexOrderByYear (line 268) | def testIndexOrderByYear(self):
method testIndexOrderByGenre (line 292) | def testIndexOrderByGenre(self):
method testIndexOrderByLength (line 322) | def testIndexOrderByLength(self):
method testCount (line 346) | def testCount(self):
method testCountAndPage (line 371) | def testCountAndPage(self):
method testGetNextSongRandom (line 403) | def testGetNextSongRandom(self):
method testGetNextSongFromQueue (line 417) | def testGetNextSongFromQueue(self):
FILE: jukebox/jukebox_core/tests/api_years.py
class ApiYearsTest (line 7) | class ApiYearsTest(ApiTestBase):
method testIndexEmpty (line 8) | def testIndexEmpty(self):
method testIndex (line 17) | def testIndex(self):
method testIndexOrderBy (line 30) | def testIndexOrderBy(self):
method testCount (line 56) | def testCount(self):
method testCountAndPage (line 84) | def testCountAndPage(self):
FILE: jukebox/jukebox_core/utils.py
class FileIndexer (line 8) | class FileIndexer:
method index (line 9) | def index(self, filename):
method delete (line 69) | def delete(self, filename):
method is_indexed (line 75) | def is_indexed(self, filename):
FILE: jukebox/jukebox_core/views.py
class JukeboxAPIView (line 13) | class JukeboxAPIView(APIView):
method api_set_user_id (line 14) | def api_set_user_id(self, request, api):
class songs (line 20) | class songs(JukeboxAPIView):
method get (line 23) | def get(self, request):
class songs_current (line 87) | class songs_current(JukeboxAPIView):
method get (line 90) | def get(self, request):
class songs_skip (line 105) | class songs_skip(JukeboxAPIView):
method get (line 108) | def get(self, request):
class artists (line 115) | class artists(JukeboxAPIView):
method get (line 118) | def get(self, request):
class albums (line 145) | class albums(JukeboxAPIView):
method get (line 148) | def get(self, request):
class genres (line 175) | class genres(JukeboxAPIView):
method get (line 178) | def get(self, request):
class years (line 205) | class years(JukeboxAPIView):
method get (line 208) | def get(self, request):
class history (line 235) | class history(JukeboxAPIView):
method get (line 238) | def get(self, request):
class history_my (line 266) | class history_my(JukeboxAPIView):
method get (line 269) | def get(self, request):
class queue (line 297) | class queue(JukeboxAPIView):
method get (line 301) | def get(self, request):
method post (line 334) | def post(self, request):
class queue_item (line 359) | class queue_item(JukeboxAPIView):
method get (line 363) | def get(self, request, song_id):
method delete (line 384) | def delete(self, request, song_id):
class favourites (line 400) | class favourites(JukeboxAPIView):
method get (line 404) | def get(self, request):
method post (line 437) | def post(self, request):
class favourites_item (line 462) | class favourites_item(JukeboxAPIView):
method get (line 466) | def get(self, request, song_id):
method delete (line 487) | def delete(self, request, song_id):
class ping (line 504) | class ping(JukeboxAPIView):
method get (line 505) | def get(self, request):
FILE: jukebox/jukebox_web/views.py
function index (line 12) | def index(request):
function login (line 30) | def login(request):
function login_error (line 42) | def login_error(request):
function logout (line 50) | def logout(request):
function language (line 54) | def language(request, language):
Condensed preview — 63 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (277K chars).
[
{
"path": ".gitignore",
"chars": 91,
"preview": ".idea/\ndist/\njukebox.egg-info/\n*.pyc\n*.sqlite\n*.pid\njukebox/settings_local.py\nbuild/\n*.sw*\n"
},
{
"path": "CHANGES.txt",
"chars": 959,
"preview": "v0.1.0, 2011-11-17 -- Initial release\nv0.1.1, 2011-11-21 -- Fixed installer bugs, added personal history, added system t"
},
{
"path": "LICENSE.rst",
"chars": 1091,
"preview": "The MIT License\n\nCopyright (c) Jens Nistler, http://jensnistler.de\n\nPermission is hereby granted, free of charge, to any"
},
{
"path": "MANIFEST.in",
"chars": 262,
"preview": "include CHANGES.txt LICENSE.rst README.rst\nrecursive-include jukebox/jukebox_core/docs *\nrecursive-include jukebox/jukeb"
},
{
"path": "README.rst",
"chars": 5840,
"preview": "Unmaintained\n============\n\nThe big time of local mp3 libraries is over. Therefore this project has been abandoned and is"
},
{
"path": "bin/jukebox",
"chars": 352,
"preview": "#!/usr/bin/env python\n\nimport imp\nimport sys\n\ntry:\n path = imp.find_module('jukebox')[1]\n sys.path.append(path)\nex"
},
{
"path": "jukebox/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "jukebox/jukebox.wsgi",
"chars": 281,
"preview": "import os\nimport sys\n\npath = os.path.normpath(os.path.dirname(__file__))\nif path not in sys.path:\n sys.path.append(pa"
},
{
"path": "jukebox/jukebox_core/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "jukebox/jukebox_core/admin.py",
"chars": 1098,
"preview": "# -*- coding: UTF-8 -*-\n\nfrom models import Artist, Genre, Album, Song, Queue, History, Favourite\nfrom django.contrib im"
},
{
"path": "jukebox/jukebox_core/api.py",
"chars": 30480,
"preview": "# -*- coding: UTF-8 -*-\nfrom django.core.paginator import Paginator, InvalidPage\nfrom django.core.exceptions import Obje"
},
{
"path": "jukebox/jukebox_core/docs/API.rst",
"chars": 2967,
"preview": "API\n=====\n\nJukebox core provides a REST API for authenticated users to control the jukebox.\nPlease register a Django Use"
},
{
"path": "jukebox/jukebox_core/forms.py",
"chars": 4016,
"preview": "# -*- coding: UTF-8 -*-\n\nfrom django import forms\n\n\nclass IdForm(forms.Form):\n id = forms.IntegerField(\n requi"
},
{
"path": "jukebox/jukebox_core/management/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "jukebox/jukebox_core/management/commands/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "jukebox/jukebox_core/management/commands/jukebox_index.py",
"chars": 1277,
"preview": "# -*- coding: UTF-8 -*-\nfrom django.core.management.base import BaseCommand\nfrom optparse import make_option\nimport os\nf"
},
{
"path": "jukebox/jukebox_core/management/commands/jukebox_setup.py",
"chars": 5158,
"preview": "# -*- coding: UTF-8 -*-\nfrom django.core.management.base import BaseCommand\nfrom django.conf import settings\n\n\nclass Com"
},
{
"path": "jukebox/jukebox_core/migrations/0001_initial.py",
"chars": 12995,
"preview": "# -*- coding: utf-8 -*-\nimport datetime\nfrom south.db import db\nfrom south.v2 import SchemaMigration\nfrom django.db impo"
},
{
"path": "jukebox/jukebox_core/migrations/0002_auto__del_field_album_Artist.py",
"chars": 7369,
"preview": "# -*- coding: utf-8 -*-\nimport datetime\nfrom south.db import db\nfrom south.v2 import SchemaMigration\nfrom django.db impo"
},
{
"path": "jukebox/jukebox_core/migrations/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "jukebox/jukebox_core/models.py",
"chars": 2500,
"preview": "# -*- coding: UTF-8 -*-\n\nfrom django.db import models\nfrom django.contrib.auth.models import User\nfrom django.contrib.sy"
},
{
"path": "jukebox/jukebox_core/tests/__init__.py",
"chars": 236,
"preview": "# -*- coding: UTF-8 -*-\n\nfrom .api_songs import *\nfrom .api_artists import *\nfrom .api_albums import *\nfrom .api_genres "
},
{
"path": "jukebox/jukebox_core/tests/api.py",
"chars": 2356,
"preview": "# -*- coding: UTF-8 -*-\n\nfrom django.test import TestCase, Client\nfrom django.db import transaction\nimport base64\nfrom j"
},
{
"path": "jukebox/jukebox_core/tests/api_albums.py",
"chars": 3434,
"preview": "# -*- coding: UTF-8 -*-\n\nimport simplejson\nfrom jukebox.jukebox_core.tests.api import ApiTestBase\n\n\nclass ApiAlbumsTest("
},
{
"path": "jukebox/jukebox_core/tests/api_artists.py",
"chars": 3437,
"preview": "# -*- coding: UTF-8 -*-\n\nimport simplejson\nfrom jukebox.jukebox_core.tests.api import ApiTestBase\n\n\nclass ApiArtistsTest"
},
{
"path": "jukebox/jukebox_core/tests/api_favourites.py",
"chars": 12003,
"preview": "# -*- coding: UTF-8 -*-\n\nimport simplejson\nfrom jukebox.jukebox_core.tests.api import ApiTestBase\n\n# ATTENTION: order te"
},
{
"path": "jukebox/jukebox_core/tests/api_genres.py",
"chars": 3395,
"preview": "# -*- coding: UTF-8 -*-\n\nimport simplejson\nfrom jukebox.jukebox_core.tests.api import ApiTestBase\n\n\nclass ApiGenresTest("
},
{
"path": "jukebox/jukebox_core/tests/api_history.py",
"chars": 18478,
"preview": "# -*- coding: UTF-8 -*-\n\nimport simplejson\nfrom jukebox.jukebox_core import api\nfrom jukebox.jukebox_core.tests.api impo"
},
{
"path": "jukebox/jukebox_core/tests/api_queue.py",
"chars": 10905,
"preview": "# -*- coding: UTF-8 -*-\n\nimport simplejson\nfrom jukebox.jukebox_core.tests.api import ApiTestBase\n\nclass ApiQueueTest(Ap"
},
{
"path": "jukebox/jukebox_core/tests/api_songs.py",
"chars": 14765,
"preview": "# -*- coding: UTF-8 -*-\n\nimport random, simplejson\nfrom jukebox.jukebox_core import api\nfrom jukebox.jukebox_core.tests."
},
{
"path": "jukebox/jukebox_core/tests/api_years.py",
"chars": 3754,
"preview": "# -*- coding: UTF-8 -*-\n\nimport simplejson\nfrom jukebox.jukebox_core.tests.api import ApiTestBase\n\n\nclass ApiYearsTest(A"
},
{
"path": "jukebox/jukebox_core/urls.py",
"chars": 1768,
"preview": "# -*- coding: UTF-8 -*-\n\nfrom django.conf.urls import patterns, url\nimport views\n\nurlpatterns = patterns(\"\",\n url(\n "
},
{
"path": "jukebox/jukebox_core/utils.py",
"chars": 2618,
"preview": "# -*- coding: UTF-8 -*-\nfrom jukebox.jukebox_core.models import Artist, Album, Song, Genre\nfrom mutagen.easyid3 import E"
},
{
"path": "jukebox/jukebox_core/views.py",
"chars": 16732,
"preview": "# -*- coding: UTF-8 -*-\n\nfrom django.core.urlresolvers import reverse\nfrom django.core.exceptions import ObjectDoesNotEx"
},
{
"path": "jukebox/jukebox_web/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "jukebox/jukebox_web/locale/de/LC_MESSAGES/django.po",
"chars": 3169,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "jukebox/jukebox_web/locale/de/LC_MESSAGES/djangojs.po",
"chars": 2762,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "jukebox/jukebox_web/locale/en/LC_MESSAGES/django.po",
"chars": 3024,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "jukebox/jukebox_web/locale/en/LC_MESSAGES/djangojs.po",
"chars": 2656,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "jukebox/jukebox_web/locale/fr/LC_MESSAGES/django.po",
"chars": 3021,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "jukebox/jukebox_web/locale/fr/LC_MESSAGES/djangojs.po",
"chars": 2692,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "jukebox/jukebox_web/locale/pt_BR/LC_MESSAGES/django.po",
"chars": 3084,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "jukebox/jukebox_web/locale/pt_BR/LC_MESSAGES/djangojs.po",
"chars": 2756,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "jukebox/jukebox_web/static/css/music.css",
"chars": 8673,
"preview": "body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, form, fieldset, input, textarea, p, blockquote, th, td, "
},
{
"path": "jukebox/jukebox_web/static/js/music.js",
"chars": 39157,
"preview": "if (typeof gettext != \"function\") {\n gettext = function(identifier) {\n return identifier;\n }\n}\n\nMusic = {\n "
},
{
"path": "jukebox/jukebox_web/templates/index.html",
"chars": 7377,
"preview": "{% load i18n %}\n<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\n <head>\n <title>{% trans \"Democrat"
},
{
"path": "jukebox/jukebox_web/templates/login.html",
"chars": 1775,
"preview": "{% load i18n %}\n<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\n <head>\n <title>{% trans 'Democrat"
},
{
"path": "jukebox/jukebox_web/urls.py",
"chars": 749,
"preview": "# -*- coding: UTF-8 -*-\n\nfrom django.conf.urls import patterns, url\nfrom jukebox.jukebox_core.models import QueueFeed\nim"
},
{
"path": "jukebox/jukebox_web/views.py",
"chars": 2037,
"preview": "# -*- coding: UTF-8 -*-\n\nfrom django.shortcuts import render_to_response\nfrom django.core.context_processors import csrf"
},
{
"path": "jukebox/manage.py",
"chars": 503,
"preview": "#!/usr/bin/env python\nfrom django.core.management import execute_manager\nimport imp\ntry:\n imp.find_module('settings')"
},
{
"path": "jukebox/settings.py",
"chars": 2539,
"preview": "import os\nimport pkgutil\nimport sys\n\nBASE_DIR = os.path.normpath(os.path.dirname(__file__))\n\nDEBUG = False\nTEMPLATE_DEBU"
},
{
"path": "jukebox/settings_local.example.py",
"chars": 290,
"preview": "ADMINS = (\n (\"[admin_user]\", \"[admin_email]\"),\n)\n\nDEBUG = True\nTEMPLATE_DEBUG = DEBUG\n\nSECRET_KEY = \"yourSecretKey\"\n\n"
},
{
"path": "jukebox/urls.py",
"chars": 422,
"preview": "# -*- coding: UTF-8 -*-\n\nfrom django.conf.urls import patterns, include, url\nfrom django.contrib import admin\nadmin.auto"
},
{
"path": "requirements.txt",
"chars": 113,
"preview": "Django==1.4.5\nmutagen==1.21\ndjango-social-auth==0.7.20\ndjangorestframework==2.2.1\nsimplejson==3.1.0\nSouth==0.7.6\n"
},
{
"path": "setup.py",
"chars": 1215,
"preview": "# -*- coding: UTF-8 -*-\nimport glob\nfrom setuptools import setup, find_packages\n\nsetup(\n name=\"jukebox\",\n packages"
}
]
// ... and 8 more files (download for full content)
About this extraction
This page contains the full source code of the lociii/jukebox GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 63 files (252.6 KB), approximately 61.6k tokens, and a symbol index with 244 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.