[
  {
    "path": ".gitignore",
    "content": ".idea/\ndist/\njukebox.egg-info/\n*.pyc\n*.sqlite\n*.pid\njukebox/settings_local.py\nbuild/\n*.sw*\n"
  },
  {
    "path": "CHANGES.txt",
    "content": "v0.1.0, 2011-11-17 -- Initial release\nv0.1.1, 2011-11-21 -- Fixed installer bugs, added personal history, added system tests for api\nv0.2.0, 2011-11-24 -- Language switch, sortable lists, google-like search operators, autoplay tries to play appropriate music, improved web interface\nv0.2.1, 2011-11-24 -- fixed issue with autoplay\nv0.3.0, 2011-11-28 -- Added jukebox_watch, added list of voters, minor improvements\nv0.3.1, 2012-09-11 -- Improved exception handling, added rss for current song, minor bug fixes\nv0.3.2, 2013-02-24 -- Update dependencies, fix authentication problems, switch from inotify to watchdog\nv0.3.3, 2013-02-28 -- Fix manifest\nv0.3.4, 2013-04-12 -- Fix to skip unauthorized sessions, updated wsgi handler\nv0.3.5, 2013-05-17 -- Update mutagen, fixed minor bugs\nv0.3.7, 2013-05-17 -- Fix buggy pypi package\nv0.4.0, 2013-06-25 -- Split jukebox in different packages, Strip artist from album data\nv0.4.1, 2013-06-25 -- Add missing wsgi file\n"
  },
  {
    "path": "LICENSE.rst",
    "content": "The MIT License\n\nCopyright (c) Jens Nistler, http://jensnistler.de\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "MANIFEST.in",
    "content": "include CHANGES.txt LICENSE.rst README.rst\nrecursive-include jukebox/jukebox_core/docs *\nrecursive-include jukebox/jukebox_web/locale *\nrecursive-include jukebox/jukebox_web/static *\nrecursive-include jukebox/jukebox_web/templates *\ninclude jukebox/jukebox.wsgi\n"
  },
  {
    "path": "README.rst",
    "content": "Unmaintained\n============\n\nThe big time of local mp3 libraries is over. Therefore this project has been abandoned and is currently unmaintained.\nMaybe have a look at https://qca.st/ which provides jukebox functionalities for music streaming services.\n\nDemocratic Jukebox - your democratic music player\n==================================================\n\nEver wanted to listen to music with a larger group of people e.g. in your office? Who decides what to play?\nMake your music player democratic and give everyone the chance to promote their favourite song.\n\nJukebox provides a web interface to search your music library and vote for songs to be played.\nThe more votes a song gets, the sooner you will listen to it.\n\nAt one point in your life your play queue might get empty. Don't worry, the jukebox will keep on playing.\nThe playback system figures out who is online using the web interface or API and plays music to their liking.\n\n**Required system libraries**\n\nlibshout3, libshout3-dev and python-dev are required to build the dependecy `python-shout <http://pypi.python.org/pypi/python-shout>`_.\n\n.. image:: http://static.jensnistler.de/jukebox.png\n   :height: 404px\n   :width: 872px\n   :scale: 100%\n   :alt: Democratic Jukebox - your democratic music player\n\nGeneral\n========\n\n- Jukebox is available in english, german and brazilian portuguese\n- Jukebox uses Facebook, Twitter and Github for authentication (see `django-social-auth <https://github.com/omab/django-social-auth>`_ for more authentication providers)\n\nSetup\n==================\n\nInstall `virtualenvwrapper <https://pypi.python.org/pypi/virtualenvwrapper>`_ via `pip <http://pypi.python.org/pypi/pip>`_ if not alreay done:\n\n::\n\n    sudo pip install virtualenvwrapper\n\nSet up a project for jukebox:\n\n::\n\n    mkproject jukebox\n\nInstall the jukebox in your fresh virtual environment:\n\n::\n\n    workon jukebox\n    pip install jukebox\n\nNow it's time to configure the jukebox\n\n1. Enter admin credentials and select authentication providers\n2. Create the database\n3. Index your music\n\nThat's all\n\n::\n\n    jukebox jukebox_setup\n    jukebox syncdb\n    jukebox migrate\n    jukebox jukebox_index --path=/path/to/library\n\nThe django builtin development webserver will be sufficient to serve your office or party. Just start it up:\n\n::\n\n    jukebox runserver ip:port\n\nNow you're ready to put music in the queue.\n\nPlayback\n=========\n\nCurrently there are two methods of playing the music chosen in jukebox.\n\n**shoutcast**\n\nStream your music to a shoutcast compatible server\n\n::\n\n    pip install jukebox-shout\n\nSee `jukebox_shout <https://github.com/lociii/jukebox_shout>`_ for details and startup command.\n\n**mpg123**\n\nPlay your music locally on the machine running the jukebox.\n\n::\n\n    pip install jukebox-mpg123\n\nSee `jukebox_mpg123 <https://github.com/lociii/jukebox_mpg123>`_ for details and startup command.\n\n**Contribute!**\n\nFeel free to write additional playback modules and I'll add them to the list above.\n\nLive indexing\n===============\n\nThere is no need to update your index every time a new song is added to your library, just use the live indexer package.\n\n::\n\n    pip install jukebox-live-indexer\n\nSee `jukebox_live_indexer <https://github.com/lociii/jukebox_live_indexer>`_ for details and startup command.\n\nAPI\n=============\n\njukebox_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>`_\n\nSearch filters\n===============\n\nJukebox supports google-like search filter. Available search fields: title, artist, album, genre, year.\n\n::\n\n    title:(love to dance) artist:bobby\n    artist:(bobby baby) lucky\n    title:(in ten years) genre:electronic\n\nLicense\n========\n\nMIT License. See `License <https://github.com/lociii/jukebox/blob/master/LICENSE.rst>`_\n\nContribute!\n============\n\nYou want to contribute to this project? Just fork the repo and do this:\n\n::\n\n    mkproject jukebox\n    git clone git@github.com:[username]/jukebox.git .\n    git remote add upstream git://github.com/lociii/jukebox.git\n    pip install -r requirements.txt\n    cd jukebox\n\nFollow up configuring jukebox like described in Setup. Use ./manage.py instead of the jukebox command.\n\nYou 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.\n\nContributors\n=============\n- Brazilian portuguese translation by `Luan Fonseca de Farias <https://github.com/luanfonceca>`_\n- Bugfixes by `Peter Hoffmann <https://github.com/hoffmann>`_\n- Bugfixes by `Amir H. Hajizamani <https://github.com/amirhhz>`_\n- Bugfixes by `Gabriel Duman <https://github.com/gabber7>`_\n- Bugfixes by `Steffen Zieger <https://github.com/saz>`_\n- Bugfixes by `Jonas Baumann <https://github.com/jone>`_\n- Bugfixes by `imithun <https://github.com/imithun>`_\n\nRelease Notes\n==============\n\n0.1.0\n\n- Initial release\n\n0.1.1\n\n- Fixed installer bugs\n- Added personal history\n- Added system tests for api\n\n0.2.0\n\n- Language switch\n- Sortable lists\n- Google-like search operators\n- Autoplay tries to play appropriate music\n- Improved web interface\n\n0.2.1\n\n- fixed issue with autoplay\n\n0.3.0\n\n- Added jukebox_watch\n- Added list of voters\n- Minor improvements\n\n0.3.1\n\n- Improved exception handling\n- Added rss for current song\n- Minor bug fixes\n\n0.3.2\n\n- Update dependencies\n- Fix authentication problems\n- Switch from inotify to watchdog\n\n0.3.3\n\n- Fix manifest\n\n0.3.4\n\n- Fix to skip unauthorized sessions\n- Updated wsgi handler\n\n0.3.5\n\n- Update mutagen (Thanks guys for removing old packages)\n- Fixed minor bugs (Thanks to `saz <https://github.com/saz/>`_)\n\n0.3.7\n\n- Fix buggy pypi package\n\n0.4.0\n\n- Split jukebox in different packages\n- Strip artist from album data\n\n0.4.1\n\n- Add missing wsgi file\n"
  },
  {
    "path": "bin/jukebox",
    "content": "#!/usr/bin/env python\n\nimport imp\nimport sys\n\ntry:\n    path = imp.find_module('jukebox')[1]\n    sys.path.append(path)\nexcept ImportError, e:\n    sys.stderr.write(\"Can't find jukebox: \" + str(e.message) + \"\\n\")\n    sys.exit(1)\n\nfrom django.core.management import execute_manager\nimport settings\n\nif __name__ == '__main__':\n    execute_manager(settings)\n"
  },
  {
    "path": "jukebox/__init__.py",
    "content": ""
  },
  {
    "path": "jukebox/jukebox.wsgi",
    "content": "import os\nimport sys\n\npath = os.path.normpath(os.path.dirname(__file__))\nif path not in sys.path:\n    sys.path.append(path)\n\nos.environ.setdefault('DJANGO_SETTINGS_MODULE', 'jukebox.settings')\n\nfrom django.core.wsgi import get_wsgi_application\napplication = get_wsgi_application()\n"
  },
  {
    "path": "jukebox/jukebox_core/__init__.py",
    "content": ""
  },
  {
    "path": "jukebox/jukebox_core/admin.py",
    "content": "# -*- coding: UTF-8 -*-\n\nfrom models import Artist, Genre, Album, Song, Queue, History, Favourite\nfrom django.contrib import admin\n\n\nclass ArtistAdmin(admin.ModelAdmin):\n    list_display = ('Name', )\n    search_fields = ['Name']\n\n\nclass GenreAdmin(admin.ModelAdmin):\n    list_display = ('Name', )\n\n\nclass AlbumAdmin(admin.ModelAdmin):\n    list_display = ('Title', )\n    search_fields = ['Title']\n\n\nclass SongAdmin(admin.ModelAdmin):\n    list_display = ('Title', 'Artist', 'Year', 'Genre', )\n    search_fields = ['Title']\n\n\nclass QueueAdmin(admin.ModelAdmin):\n    list_display = ('Song', 'Created', )\n\n\nclass HistoryAdmin(admin.ModelAdmin):\n    list_display = ('Song', 'Created', )\n\n\nclass FavouriteAdmin(admin.ModelAdmin):\n    list_display = ('Song', 'User', 'Created', )\n    search_fields = ['User__username']\n\n\nadmin.site.register(Artist, ArtistAdmin)\nadmin.site.register(Genre, GenreAdmin)\nadmin.site.register(Album, AlbumAdmin)\nadmin.site.register(Song, SongAdmin)\nadmin.site.register(Queue, QueueAdmin)\nadmin.site.register(History, HistoryAdmin)\nadmin.site.register(Favourite, FavouriteAdmin)\n"
  },
  {
    "path": "jukebox/jukebox_core/api.py",
    "content": "# -*- coding: UTF-8 -*-\nfrom django.core.paginator import Paginator, InvalidPage\nfrom django.core.exceptions import ObjectDoesNotExist\nfrom django.db import transaction\nfrom django.db.models import Count, Min, Q\nfrom django.contrib.sessions.models import Session\nfrom django.utils import formats\nimport os, re, time\nfrom datetime import datetime\nfrom signal import SIGABRT\nfrom django.contrib.auth.models import User\nfrom models import Song, Artist, Album, Genre, Queue, Favourite, History, Player\n\n\nclass api_base:\n    count = 30\n    user_id = None\n    search_term = None\n    search_title = None\n    search_artist_name = None\n    search_album_title = None\n    filter_year = None\n    filter_genre = None\n    filter_album_id = None\n    filter_artist_id = None\n    order_by_field = None\n    order_by_direction = None\n    order_by_fields = []\n    order_by_directions = [\"asc\", \"desc\"]\n    order_by_default = None\n\n    def set_count(self, count):\n        if count > 100:\n            self.count = 100\n        elif count > 0:\n            self.count = count\n\n    def set_user_id(self, user_id):\n        self.user_id = user_id\n\n    def set_search_term(self, term):\n        options = self.parseSearchString(\n            (\n                \"title\",\n                \"artist\",\n                \"album\",\n                \"genre\",\n                \"year\",\n            ),\n            term\n        )\n        for key, value in options.items():\n            if key == \"title\":\n                self.set_search_title(value)\n            elif key == \"artist\":\n                self.set_search_artist_name(value)\n            elif key == \"album\":\n                self.set_search_album_title(value)\n            elif key == \"genre\":\n                try:\n                    genre = Genre.objects.all().filter(Name__iexact=value)[0:1].get()\n                    self.set_filter_genre(genre.id)\n                except ObjectDoesNotExist:\n                    pass\n            elif key == \"year\":\n                self.set_filter_year(value)\n\n        self.search_term = options[\"term\"]\n\n    def parseSearchString(self, keywords, term):\n        values = {}\n        for i in range(len(keywords)):\n            do_continue = False\n            keyword = keywords[i]\n            value = None\n            pos = term.find(keyword + \":\")\n            if pos != -1:\n                value_start = pos + len(keyword) + 1\n                # no brackets, search for next whitespace\n                if term[value_start:value_start + 1] != \"(\":\n                    value_end = term.find(\" \", value_start)\n                    if value_end == -1:\n                        value_end = len(term)\n                        do_continue = True\n                    value = term[value_start:value_end]\n                # search for next closing bracket but count opened ones\n                else:\n                    i = value_start + 1\n                    bracket_count = 1\n                    while i < len(term):\n                        char = term[i:i+1]\n                        if char == \"(\":\n                            bracket_count+= 1\n                        elif char == \")\":\n                            bracket_count-= 1\n                            if not bracket_count:\n                                value = term[value_start:i+1]\n                                continue\n                        i+= 1\n\n                    if not value:\n                        value = term[value_start:len(term)]\n                        do_continue = True\n\n            if value is not None:\n                values[keyword] = value\n            if do_continue:\n                continue\n\n        for key, value in values.items():\n            term = term.replace(key + \":\" + value, \"\").strip()\n            if value.startswith(\"(\"):\n                values[key] = value[1:len(value)-1]\n\n        values[\"term\"] = re.sub(\"\\s+\", \" \", term)\n        return values\n\n    def set_search_title(self, term):\n        self.search_title = term\n\n    def set_search_artist_name(self, term):\n        self.search_artist_name = term\n\n    def set_search_album_title(self, term):\n        self.search_album_title = term\n\n    def set_filter_year(self, term):\n        self.filter_year = term\n\n    def set_filter_genre(self, term):\n        self.filter_genre = term\n\n    def set_filter_album_id(self, term):\n        self.filter_album_id = term\n\n    def set_filter_artist_id(self, term):\n        self.filter_artist_id = term\n\n    def set_order_by(self, field, direction=\"asc\"):\n        if (not field in self.order_by_fields or\n            not direction in self.order_by_directions):\n            return\n\n        self.order_by_field = field\n        self.order_by_direction = direction\n\n    def get_default_result(self, result_type, page):\n        search = {}\n        if self.search_title is not None:\n            value = self.search_title\n            if value.find(\" \") != -1:\n                value = \"(\" + value + \")\"\n            search[\"title\"] = value\n        if self.search_artist_name is not None:\n            value = self.search_artist_name\n            if value.find(\" \") != -1:\n                value = \"(\" + value + \")\"\n            search[\"artist\"] = value\n        if self.search_album_title is not None:\n            value = self.search_album_title\n            if value.find(\" \") != -1:\n                value = \"(\" + value + \")\"\n            search[\"album\"] = value\n        if self.filter_genre is not None:\n            genre = Genre.objects.all().filter(id=self.filter_genre)[0:1].get()\n            value = genre.Name\n            if value.find(\" \") != -1:\n                value = \"(\" + value + \")\"\n            search[\"genre\"] = value\n            search[\"genre_id\"] = genre.id\n        if self.filter_year is not None:\n            search[\"year\"] = str(self.filter_year)\n        if self.search_term is not None:\n            search[\"term\"] = self.search_term\n\n        return {\n            \"type\": result_type,\n            \"page\": page,\n            \"hasNextPage\": False,\n            \"itemList\": [],\n            \"order\": [],\n            \"search\": search,\n        }\n\n    def result_add_queue_and_favourite(self, song, dataset):\n        if not self.user_id is None:\n            try:\n                queue = Queue.objects.get(Song=song)\n                for user in queue.User.all():\n                    if user.id == self.user_id:\n                        dataset[\"queued\"] = True\n                        break\n            except ObjectDoesNotExist:\n                pass\n            try:\n                user = User.objects.get(id=self.user_id)\n                Favourite.objects.get(Song=song, User=user)\n                dataset[\"favourite\"] = True\n            except ObjectDoesNotExist:\n                pass\n\n        return dataset\n\n    def source_set_order(self, object_list):\n        if not self.order_by_field is None:\n            field_name = self.order_by_fields.get(self.order_by_field)\n            if self.order_by_direction == \"desc\":\n                field_name = \"-\" + field_name\n\n            return object_list.order_by(field_name)\n        elif not self.order_by_default is None:\n            order = []\n            for key, value in self.order_by_default.items():\n                order.append(value)\n\n            object_list = object_list.order_by(*order)\n\n        return object_list\n\n    def result_set_order(self, result):\n        result[\"order\"] = []\n\n        if not self.order_by_field is None:\n            result[\"order\"].append({\n                \"field\": self.order_by_field,\n                \"direction\": self.order_by_direction,\n            })\n        elif not self.order_by_default is None:\n            for field, order in self.order_by_default.items():\n                result[\"order\"].append({\n                    \"field\": field,\n                    \"direction\": \"desc\" if order.startswith(\"-\") else \"asc\",\n                })\n\n        return result\n\nclass songs(api_base):\n    order_by_fields = {\n        \"title\": \"Title\",\n        \"artist\": \"Artist__Name\",\n        \"album\": \"Album__Title\",\n        \"year\": \"Year\",\n        \"genre\": \"Genre__Name\",\n        \"length\": \"Length\",\n    }\n    order_by_default = {\n        \"title\": \"Title\",\n    }\n\n    def index(self, page=1):\n        object_list = Song.objects.all()\n\n        # searches\n        if not self.search_term is None:\n            object_list = object_list.filter(\n                Q(Title__contains=self.search_term)\n                |\n                Q(Artist__Name__contains=self.search_term)\n                |\n                Q(Album__Title__contains=self.search_term)\n            )\n        if not self.search_title is None:\n            object_list = object_list.filter(\n                 Title__contains=self.search_title\n             )\n        if not self.search_artist_name is None:\n            object_list = object_list.filter(\n                 Artist__Name__contains=self.search_artist_name\n             )\n        if not self.search_album_title is None:\n            object_list = object_list.filter(\n                 Album__Title__contains=self.search_album_title\n             )\n\n        # filters\n        if not self.filter_year is None:\n            object_list = object_list.filter(\n                 Year__exact=self.filter_year\n             )\n        if not self.filter_genre is None:\n            object_list = object_list.filter(\n                 Genre__exact=self.filter_genre\n             )\n        if not self.filter_album_id is None:\n            object_list = object_list.filter(\n                 Album__exact=self.filter_album_id\n             )\n        if not self.filter_artist_id is None:\n            object_list = object_list.filter(\n                 Artist__exact=self.filter_artist_id\n             )\n\n        # order\n        object_list = self.source_set_order(object_list)\n\n        # prepare result\n        result = self.get_default_result(\"songs\", page)\n\n        # get data\n        paginator = Paginator(object_list, self.count)\n        try:\n            page_obj = paginator.page(page)\n        except InvalidPage:\n            return result\n\n        result = self.result_set_order(result)\n        result[\"hasNextPage\"] = page_obj.has_next()\n        for item in page_obj.object_list:\n            dataset = {\n                \"id\": item.id,\n                \"title\": None,\n                \"artist\": {\n                    \"id\": None,\n                    \"name\": None,\n                },\n                \"album\": {\n                    \"id\": None,\n                    \"title\": None,\n                },\n                \"year\": None,\n                \"genre\": {\n                    \"id\": None,\n                    \"name\": None,\n                },\n                \"length\": None,\n                \"queued\": False,\n                \"favourite\": False,\n            }\n            if not item.Title is None:\n                dataset[\"title\"] = item.Title\n            if not item.Artist is None:\n                dataset[\"artist\"][\"id\"] = item.Artist.id\n                dataset[\"artist\"][\"name\"] = item.Artist.Name\n            if not item.Album is None:\n                dataset[\"album\"][\"id\"] = item.Album.id\n                dataset[\"album\"][\"title\"] = item.Album.Title\n            if not item.Year is None:\n                dataset[\"year\"] = item.Year\n            if not item.Genre is None:\n                dataset[\"genre\"][\"id\"] = item.Genre.id\n                dataset[\"genre\"][\"name\"] = item.Genre.Name\n            if not item.Length is None:\n                dataset[\"length\"] = item.Length\n\n            dataset = self.result_add_queue_and_favourite(item, dataset)\n            result[\"itemList\"].append(dataset)\n\n        return result\n\n    def getNextSong(self):\n        # commit transaction to force fresh queryset result\n        try:\n            transaction.enter_transaction_management()\n            transaction.commit()\n        except BaseException:\n            pass\n\n        try:\n            data = Queue.objects.all()\n            data = data.annotate(VoteCount=Count(\"User\"))\n            data = data.annotate(MinCreated=Min(\"Created\"))\n            data = data.order_by(\"-VoteCount\", \"MinCreated\")[0:1].get()\n            self.addToHistory(data.Song, data.User)\n            song_instance = data.Song\n            data.delete()\n        except ObjectDoesNotExist:\n            try:\n                song_instance = self.getRandomSongByPreferences()\n                self.addToHistory(song_instance, None)\n            except ObjectDoesNotExist:\n                song_instance = Song.objects.order_by('?')[0:1].get()\n                self.addToHistory(song_instance, None)\n\n        # remove missing files\n        if not os.path.exists(song_instance.Filename.encode('utf8')):\n            Song.objects.all().filter(id=song_instance.id).delete()\n            return self.getNextSong()\n\n        return song_instance\n\n    def getRandomSongByPreferences(self):\n        artists = {}\n\n        # get logged in users\n        sessions = Session.objects.exclude(\n            expire_date__lt=datetime.today()\n        )\n        for session in sessions.all():\n            data = session.get_decoded()\n            if not \"_auth_user_id\" in data:\n                continue\n            user_id = data[\"_auth_user_id\"]\n\n            # get newest favourites\n            favourites = Favourite.objects.filter(User__id=user_id)[0:30]\n            for favourite in favourites:\n                if not favourite.Song.Artist.id in artists:\n                    artists[favourite.Song.Artist.id] = 0\n                artists[favourite.Song.Artist.id]+= 1\n\n            # get last voted songs\n            votes = History.objects.filter(User__id=user_id)[0:30]\n            for vote in votes:\n                if not vote.Song.Artist.id in artists:\n                    artists[vote.Song.Artist.id] = 0\n                artists[vote.Song.Artist.id]+= 1\n\n        # nothing played and no favourites\n        if not len(artists):\n            raise ObjectDoesNotExist\n\n        # calculate top artists\n        from operator import itemgetter\n        sorted_artists = sorted(\n            artists.iteritems(),\n            key=itemgetter(1),\n            reverse=True\n        )[0:30]\n        artists = []\n        for key in range(len(sorted_artists)):\n            artists.append(sorted_artists[key][0])\n\n        # get the 50 last played songs\n        history = History.objects.all()[0:50]\n        last_played = []\n        for item in history:\n            last_played.append(item.Song.id)\n\n        # find a song not played recently\n        song_instance = Song.objects.exclude(\n            id__in=last_played\n        ).filter(\n            Artist__id__in=artists\n        ).order_by('?')[0:1].get()\n        return song_instance\n\n    def addToHistory(self, song_instance, user_list):\n        history_instance = History(\n            Song=song_instance\n        )\n        history_instance.save()\n\n        if user_list is not None and user_list.count() > 0:\n            for user_instance in user_list.all():\n                history_instance.User.add(user_instance)\n\n    def skipCurrentSong(self):\n        players = Player.objects.all()\n        for player in players:\n            try:\n                os.kill(player.Pid, SIGABRT)\n            except OSError:\n                player.delete()\n\nclass history(api_base):\n    order_by_fields = {\n        \"title\": \"Song__Title\",\n        \"artist\": \"Song__Artist__Name\",\n        \"album\": \"Song__Album__Title\",\n        \"year\": \"Song__Year\",\n        \"genre\": \"Song__Genre__Name\",\n        \"created\": \"Created\",\n    }\n    order_by_default = {\n        \"created\": \"-Created\",\n    }\n\n    def index(self, page=1):\n        object_list = History.objects.all()\n        object_list = self.source_set_order(object_list)\n        result = self.build_result(object_list, page)\n        result = self.result_set_order(result)\n        return result\n\n    def build_result(self, object_list, page):\n        # prepare result\n        result = self.get_default_result(\"history\", page)\n\n        # get data\n        paginator = Paginator(object_list, self.count)\n        try:\n            page_obj = paginator.page(page)\n        except InvalidPage:\n            return result\n\n        result = self.result_set_order(result)\n        result[\"hasNextPage\"] = page_obj.has_next()\n        for item in page_obj.object_list:\n            dataset = {\n                \"id\": item.Song.id,\n                \"title\": None,\n                \"artist\": {\n                    \"id\": None,\n                    \"name\": None,\n                },\n                \"album\": {\n                    \"id\": None,\n                    \"title\": None,\n                },\n                \"year\": None,\n                \"genre\": {\n                    \"id\": None,\n                    \"name\": None,\n                },\n                \"queued\": False,\n                \"favourite\": False,\n                \"created\": formats.date_format(\n                    item.Created, \"DATETIME_FORMAT\"\n                ),\n                \"votes\": item.User.count(),\n                \"users\": [],\n            }\n            if not item.Song.Title is None:\n                dataset[\"title\"] = item.Song.Title\n            if not item.Song.Artist is None:\n                dataset[\"artist\"][\"id\"] = item.Song.Artist.id\n                dataset[\"artist\"][\"name\"] = item.Song.Artist.Name\n            if not item.Song.Album is None:\n                dataset[\"album\"][\"id\"] = item.Song.Album.id\n                dataset[\"album\"][\"title\"] = item.Song.Album.Title\n            if not item.Song.Year is None:\n                dataset[\"year\"] = item.Song.Year\n            if not item.Song.Genre is None:\n                dataset[\"genre\"][\"id\"] = item.Song.Genre.id\n                dataset[\"genre\"][\"name\"] = item.Song.Genre.Name\n\n            if not item.User.count() == 0:\n                for user in item.User.all():\n                    dataset[\"users\"].append({\n                        \"id\": user.id,\n                        \"name\": user.get_full_name()\n                    })\n\n            dataset = self.result_add_queue_and_favourite(item.Song, dataset)\n            result[\"itemList\"].append(dataset)\n\n        return result\n\n    def getCurrent(self):\n        item = History.objects.all()[0:1].get()\n        createdTimestamp = time.mktime(item.Created.timetuple())\n        dataset = {\n            \"id\": item.Song.id,\n            \"title\": None,\n            \"artist\": {\n                \"id\": None,\n                \"name\": None,\n            },\n            \"album\": {\n                \"id\": None,\n                \"title\": None,\n            },\n            \"year\": None,\n            \"genre\": {\n                \"id\": None,\n                \"name\": None,\n            },\n            \"queued\": False,\n            \"favourite\": False,\n            \"created\": formats.date_format(\n                item.Created, \"DATETIME_FORMAT\"\n            ),\n            \"votes\": item.User.count(),\n            \"users\": [],\n            \"remaining\": createdTimestamp + item.Song.Length - int(time.time())\n        }\n        if not item.Song.Title is None:\n            dataset[\"title\"] = item.Song.Title\n        if not item.Song.Artist is None:\n            dataset[\"artist\"][\"id\"] = item.Song.Artist.id\n            dataset[\"artist\"][\"name\"] = item.Song.Artist.Name\n        if not item.Song.Album is None:\n            dataset[\"album\"][\"id\"] = item.Song.Album.id\n            dataset[\"album\"][\"title\"] = item.Song.Album.Title\n        if not item.Song.Year is None:\n            dataset[\"year\"] = item.Song.Year\n        if not item.Song.Genre is None:\n            dataset[\"genre\"][\"id\"] = item.Song.Genre.id\n            dataset[\"genre\"][\"name\"] = item.Song.Genre.Name\n\n        return dataset\n\nclass history_my(history):\n    order_by_fields = {\n        \"title\": \"Song__Title\",\n        \"artist\": \"Song__Artist__Name\",\n        \"album\": \"Song__Album__Title\",\n        \"year\": \"Song__Year\",\n        \"genre\": \"Song__Genre__Name\",\n        \"created\": \"Created\",\n    }\n    order_by_default = {\n        \"created\": \"-Created\",\n    }\n\n    def index(self, page=1):\n        object_list = History.objects.all().filter(User__id=self.user_id)\n        object_list = self.source_set_order(object_list)\n        result = self.build_result(object_list, page)\n\n        result = self.result_set_order(result)\n        result[\"type\"] = \"history/my\"\n        return result\n\n\nclass queue(api_base):\n    order_by_fields = {\n        \"title\": \"Song__Title\",\n        \"artist\": \"Song__Artist__Name\",\n        \"album\": \"Song__Album__Title\",\n        \"year\": \"Song__Year\",\n        \"genre\": \"Song__Genre__Name\",\n        \"created\": \"Created\",\n        \"votes\": \"VoteCount\",\n    }\n    order_by_default = {\n        \"votes\": \"-VoteCount\",\n        \"created\": \"MinCreated\",\n    }\n\n    def index(self, page=1):\n        object_list = Queue.objects.all()\n        object_list = object_list.annotate(VoteCount=Count(\"User\"))\n        object_list = object_list.annotate(MinCreated=Min(\"Created\"))\n        object_list = self.source_set_order(object_list)\n\n        # prepare result\n        result = self.get_default_result(\"queue\", page)\n\n        # get data\n        paginator = Paginator(object_list, self.count)\n        try:\n            page_obj = paginator.page(page)\n        except InvalidPage:\n            return result\n\n        result = self.result_set_order(result)\n        result[\"hasNextPage\"] = page_obj.has_next()\n        for item in page_obj.object_list:\n            result[\"itemList\"].append(self.get(item.Song.id))\n\n        return result\n\n    def get(self, song_id):\n        song = Song.objects.get(id=song_id)\n        item = Queue.objects.get(Song=song)\n\n        result = {\n            \"id\": item.Song.id,\n            \"title\": None,\n            \"artist\": {\n                \"id\": None,\n                \"name\": None,\n            },\n            \"album\": {\n                \"id\": None,\n                \"title\": None,\n            },\n            \"year\": None,\n            \"genre\": {\n                \"id\": None,\n                \"name\": None,\n            },\n            \"queued\": False,\n            \"favourite\": False,\n            \"created\": formats.date_format(item.Created, \"DATETIME_FORMAT\"),\n            \"votes\": item.User.count(),\n            \"users\": [],\n        }\n\n        if not item.Song.Title is None:\n            result[\"title\"] = item.Song.Title\n        if not item.Song.Artist is None:\n            result[\"artist\"][\"id\"] = item.Song.Artist.id\n            result[\"artist\"][\"name\"] = item.Song.Artist.Name\n        if not item.Song.Album is None:\n            result[\"album\"][\"id\"] = item.Song.Album.id\n            result[\"album\"][\"title\"] = item.Song.Album.Title\n        if not item.Song.Year is None:\n            result[\"year\"] = item.Song.Year\n        if not item.Song.Genre is None:\n            result[\"genre\"][\"id\"] = item.Song.Genre.id\n            result[\"genre\"][\"name\"] = item.Song.Genre.Name\n\n        if not item.User.count() == 0:\n            for user in item.User.all():\n                result[\"users\"].append({\"id\": user.id, \"name\": user.get_full_name()})\n\n        result = self.result_add_queue_and_favourite(item.Song, result)\n\n        return result\n\n    def add(self, song_id):\n        song = Song.objects.get(id=song_id)\n        user = User.objects.get(id=self.user_id)\n\n        try:\n            queue = Queue.objects.get(Song=song)\n        except ObjectDoesNotExist:\n            queue = Queue(\n                Song=song\n            )\n            queue.save()\n        queue.User.add(user)\n\n        return song_id\n\n    def remove(self, song_id):\n        song = Song.objects.get(id=song_id)\n        user = User.objects.get(id=self.user_id)\n\n        queue = Queue.objects.get(Song=song)\n        queue.User.remove(user)\n        vote_count = queue.User.count()\n        if not queue.User.count():\n            queue.delete()\n\n        return {\n            \"id\": song_id,\n            \"count\": vote_count,\n        }\n\n\nclass favourites(api_base):\n    order_by_fields = {\n        \"title\": \"Song__Title\",\n        \"artist\": \"Song__Artist__Name\",\n        \"album\": \"Song__Album__Title\",\n        \"year\": \"Song__Year\",\n        \"genre\": \"Song__Genre__Name\",\n        \"created\": \"Created\",\n    }\n    order_by_default = {\n        \"created\": \"-Created\",\n    }\n\n    def index(self, page=1):\n        object_list = Favourite.objects.all().filter(User__id=self.user_id)\n        object_list = self.source_set_order(object_list)\n\n        # prepare result\n        result = self.get_default_result(\"favourites\", page)\n\n        # get data\n        paginator = Paginator(object_list, self.count)\n        try:\n            page_obj = paginator.page(page)\n        except InvalidPage:\n            return result\n\n        result = self.result_set_order(result)\n        result[\"hasNextPage\"] = page_obj.has_next()\n        for item in page_obj.object_list:\n            result[\"itemList\"].append(self.get(item.Song.id))\n\n        return result\n\n    def get(self, song_id):\n        song = Song.objects.get(id=song_id)\n        item = Favourite.objects.get(Song=song,User__id=self.user_id)\n\n        result = {\n            \"id\": item.Song.id,\n            \"title\": None,\n            \"artist\": {\n                \"id\": None,\n                \"name\": None,\n            },\n            \"album\": {\n                \"id\": None,\n                \"title\": None,\n            },\n            \"year\": None,\n            \"genre\": {\n                \"id\": None,\n                \"name\": None,\n            },\n            \"queued\": False,\n            \"favourite\": False,\n            \"created\": formats.date_format(item.Created, \"DATETIME_FORMAT\"),\n        }\n\n        if not item.Song.Title is None:\n            result[\"title\"] = item.Song.Title\n        if not item.Song.Artist is None:\n            result[\"artist\"][\"id\"] = item.Song.Artist.id\n            result[\"artist\"][\"name\"] = item.Song.Artist.Name\n        if not item.Song.Album is None:\n            result[\"album\"][\"id\"] = item.Song.Album.id\n            result[\"album\"][\"title\"] = item.Song.Album.Title\n        if not item.Song.Year is None:\n            result[\"year\"] = item.Song.Year\n        if not item.Song.Genre is None:\n            result[\"genre\"][\"id\"] = item.Song.Genre.id\n            result[\"genre\"][\"name\"] = item.Song.Genre.Name\n\n        result = self.result_add_queue_and_favourite(item.Song, result)\n\n        return result\n\n    def add(self, song_id):\n        song = Song.objects.get(id=song_id)\n        user = User.objects.get(id=self.user_id)\n\n        favourite = Favourite(\n            Song=song,\n            User=user\n        )\n        favourite.save()\n\n        return song_id\n\n    def remove(self, song_id):\n        song = Song.objects.get(id=song_id)\n        user = User.objects.get(id=self.user_id)\n\n        Favourite.objects.get(\n            Song=song,\n            User=user\n        ).delete()\n\n        return {\n            \"id\": song_id,\n        }\n\n\nclass artists(api_base):\n    order_by_fields = {\n        \"artist\": \"Name\",\n    }\n    order_by_default = {\n        \"artist\": \"Name\",\n    }\n\n    def index(self, page=1):\n        # prepare result\n        result = self.get_default_result(\"artists\", page)\n\n        object_list = Artist.objects.all()\n        object_list = self.source_set_order(object_list)\n\n        # get data\n        paginator = Paginator(object_list, self.count)\n        try:\n            page_obj = paginator.page(page)\n        except InvalidPage:\n            return result\n\n        result = self.result_set_order(result)\n        result[\"hasNextPage\"] = page_obj.has_next()\n        for item in page_obj.object_list:\n            dataset = {\n                \"id\": item.id,\n                \"artist\": item.Name,\n            }\n\n            result[\"itemList\"].append(dataset)\n\n        return result\n\n\nclass albums(api_base):\n    order_by_fields = {\n        \"album\": \"Title\",\n    }\n    order_by_default = {\n        \"album\": \"Title\",\n    }\n\n    def index(self, page=1):\n        # prepare result\n        result = self.get_default_result(\"albums\", page)\n\n        object_list = Album.objects.all()\n        object_list = self.source_set_order(object_list)\n\n        # get data\n        paginator = Paginator(object_list, self.count)\n        try:\n            page_obj = paginator.page(page)\n        except InvalidPage:\n            return result\n\n        result = self.result_set_order(result)\n        result[\"hasNextPage\"] = page_obj.has_next()\n        for item in page_obj.object_list:\n            dataset = {\n                \"id\": item.id,\n                \"album\": item.Title,\n            }\n\n            result[\"itemList\"].append(dataset)\n\n        return result\n\n\nclass genres(api_base):\n    order_by_fields = {\n        \"genre\": \"Name\",\n    }\n    order_by_default = {\n        \"genre\": \"Name\",\n    }\n\n    def index(self, page=1):\n        # prepare result\n        result = self.get_default_result(\"genres\", page)\n\n        object_list = Genre.objects.all()\n        object_list = self.source_set_order(object_list)\n\n        # get data\n        paginator = Paginator(object_list, self.count)\n        try:\n            page_obj = paginator.page(page)\n        except InvalidPage:\n            return result\n\n        result = self.result_set_order(result)\n        result[\"hasNextPage\"] = page_obj.has_next()\n        for item in page_obj.object_list:\n            dataset = {\n                \"id\": item.id,\n                \"genre\": item.Name,\n            }\n\n            result[\"itemList\"].append(dataset)\n\n        return result\n\n\nclass years(api_base):\n    order_by_fields = {\n        \"year\": \"Year\",\n    }\n    order_by_default = {\n        \"year\": \"Year\"\n    }\n\n    def index(self, page=1):\n        # prepare result\n        result = self.get_default_result(\"years\", page)\n\n        object_list = Song.objects.values(\"Year\").distinct()\n        object_list = object_list.exclude(Year=None).exclude(Year=0)\n        object_list = self.source_set_order(object_list)\n\n        # get data\n        paginator = Paginator(object_list, self.count)\n        try:\n            page_obj = paginator.page(page)\n        except InvalidPage:\n            return result\n\n        result = self.result_set_order(result)\n        result[\"hasNextPage\"] = page_obj.has_next()\n        for item in page_obj.object_list:\n            dataset = {\n                \"year\": item[\"Year\"],\n            }\n\n            result[\"itemList\"].append(dataset)\n\n        return result\n\n\nclass players(api_base):\n    def add(self, pid):\n        player = Player(\n            Pid=pid\n        )\n        player.save()\n\n        return player.id\n\n    def remove(self, pid):\n        Player.objects.get(Pid=pid).delete()\n\n        return {\n            \"pid\": pid,\n        }\n"
  },
  {
    "path": "jukebox/jukebox_core/docs/API.rst",
    "content": "API\n=====\n\nJukebox core provides a REST API for authenticated users to control the jukebox.\nPlease register a Django User in the admin interface and use HTTP basic authentication for external API access.\nAlternatively the API is accessible for authenticated users sending a session key.\n\nTests\n======\n\nThe API is completely unit tested. See jukebox_core/tests\n\nGET methods\n============\n\n::\n\n    /api/v1/songs\n    /api/v1/artists\n    /api/v1/albums\n    /api/v1/genres\n    /api/v1/years\n    /api/v1/history\n    /api/v1/history/my\n    /api/v1/queue\n    /api/v1/queue/[song_id]\n    /api/v1/favourites\n    /api/v1/favourites/[song_id]\n    /api/v1/ping\n\n*Sort parameters for list functions*\n\n- order_by (field to order by, see below)\n- order_direction (\"asc\" or \"desc\", defaults to \"asc\")\n\n**/api/v1/songs**\n\nList songs\n\n*Available options*\n\n- count (item count to be returned)\n- page (page to be returned)\n- search_term (search in song title, artist name and album title)\n- search_title (search in song title)\n- search_artist (search in artist name)\n- search_album (search in album title)\n- filter_artist_id (filter by artist id)\n- filter_album_id (filter by album id)\n- filter_genre (filter by genre id)\n- filter_year (filter by release year)\n\n*Available sort options*\n\n- title (default, asc)\n- artist\n- album\n- year\n- genre\n- length\n\n**/api/v1/artists**\n\nList artists\n\n*Available sort options*\n\n- artist (default, asc)\n\n**/api/v1/albums**\n\nList albums\n\n*Available sort options*\n\n- album (default, asc)\n\n**/api/v1/genres**\n\nList genres\n\n*Available sort options*\n\n- genre (default, asc)\n\n**/api/v1/years**\n\nList years\n\n*Available sort options*\n\n- year (default, asc)\n\n**/api/v1/history**\n\nList all played songs\n\n*Available sort options*\n\n- title\n- artist\n- album\n- year\n- genre\n- created (default, desc)\n\n**/api/v1/history/my**\n\nList all played songs the authenticated user voted for\n\nSee /api/v1/history for details\n\n**/api/v1/queue**\n\nList songs in play queue sorted by vote count and first vote\n\n*Available sort options*\n\n- title\n- artist\n- album\n- year\n- genre\n- created (default, asc)\n- votes (default, desc)\n\n**/api/v1/queue/[song_id]**\n\nGet single play queue entry\n\n**/api/v1/favourites**\n\n*Available sort options*\n\n- title  (default, asc)\n- artist\n- album\n- year\n- genre\n- created\n\n**/api/v1/favourites/[song_id]**\n\nGet single favourite list entry\n\n**/api/v1/ping**\n\nPing the api for session keepalive\n\nPOST methods\n============\n\n::\n\n    /api/v1/queue\n    /api/v1/favourites\n\n**/api/v1/queue**\n\nVote for song, add to queue if not yet in\n\n*Required post parameters*\n\n- id (id of song to be added)\n\n**/api/v1/favourites**\n\nAdd song to favourite list\n\n*Required post parameters*\n\n- id (id of song to be added)\n\nDELETE methods\n===============\n\n::\n\n    /api/v1/queue/[song_id]\n    /api/v1/favourites/[song_id]\n\n**/api/v1/queue/[song_id]**\n\nRevoke vote for song, remove from queue if no more votes left\n\n**/api/v1/favourites/[song_id]**\n\nRemove song from favourite list\n"
  },
  {
    "path": "jukebox/jukebox_core/forms.py",
    "content": "# -*- coding: UTF-8 -*-\n\nfrom django import forms\n\n\nclass IdForm(forms.Form):\n    id = forms.IntegerField(\n        required=True\n    )\n\n\nclass SongsForm(forms.Form):\n    count = forms.IntegerField(\n        required=False\n    )\n    page = forms.IntegerField(\n        required=False\n    )\n\n    search_term = forms.CharField(\n        required=False\n    )\n    search_title = forms.CharField(\n        required=False\n    )\n    search_artist = forms.CharField(\n        required=False\n    )\n    search_album = forms.CharField(\n        required=False\n    )\n\n    filter_year = forms.IntegerField(\n        required=False\n    )\n    filter_genre = forms.IntegerField(\n        required=False\n    )\n    filter_album_id = forms.IntegerField(\n        required=False\n    )\n    filter_artist_id = forms.IntegerField(\n        required=False\n    )\n\n    order_by = forms.CharField(\n        max_length=6,\n        help_text=\"'title', 'artist', 'album', 'year', 'genre'\",\n        required=False\n    )\n    order_direction = forms.CharField(\n        max_length=4,\n        help_text=\"'asc', 'desc'\",\n        required=False\n    )\n\n\nclass ArtistsForm(forms.Form):\n    count = forms.IntegerField(\n        required=False\n    )\n    page = forms.IntegerField(\n        required=False\n    )\n\n    order_by = forms.CharField(\n        max_length=6,\n        help_text=\"'artist'\",\n        required=False\n    )\n    order_direction = forms.CharField(\n        max_length=4,\n        help_text=\"'asc', 'desc'\",\n        required=False\n    )\n\n\nclass AlbumsForm(forms.Form):\n    count = forms.IntegerField(\n        required=False\n    )\n    page = forms.IntegerField(\n        required=False\n    )\n\n    order_by = forms.CharField(\n        max_length=6,\n        help_text=\"'album', 'artist'\",\n        required=False\n    )\n    order_direction = forms.CharField(\n        max_length=4,\n        help_text=\"'asc', 'desc'\",\n        required=False\n    )\n\n\nclass GenresForm(forms.Form):\n    count = forms.IntegerField(\n        required=False\n    )\n    page = forms.IntegerField(\n        required=False\n    )\n\n    order_by = forms.CharField(\n        max_length=5,\n        help_text=\"'genre'\",\n        required=False\n    )\n    order_direction = forms.CharField(\n        max_length=4,\n        help_text=\"'asc', 'desc'\",\n        required=False\n    )\n\n\nclass YearsForm(forms.Form):\n    count = forms.IntegerField(\n        required=False\n    )\n    page = forms.IntegerField(\n        required=False\n    )\n\n    order_by = forms.CharField(\n        max_length=4,\n        help_text=\"'year'\",\n        required=False\n    )\n    order_direction = forms.CharField(\n        max_length=4,\n        help_text=\"'asc', 'desc'\",\n        required=False\n    )\n\n\nclass HistoryForm(forms.Form):\n    count = forms.IntegerField(\n        required=False\n    )\n    page = forms.IntegerField(\n        required=False\n    )\n\n    order_by = forms.CharField(\n        max_length=7,\n        help_text=\"'title', 'artist', 'album', 'year', 'genre', 'created'\",\n        required=False\n    )\n    order_direction = forms.CharField(\n        max_length=4,\n        help_text=\"'asc', 'desc'\",\n        required=False\n    )\n\n\nclass FavouritesForm(forms.Form):\n    count = forms.IntegerField(\n        required=False\n    )\n    page = forms.IntegerField(\n        required=False\n    )\n\n    order_by = forms.CharField(\n        max_length=7,\n        help_text=\"'title', 'artist', 'album', 'year', 'genre', 'created'\",\n        required=False\n    )\n    order_direction = forms.CharField(\n        max_length=4,\n        help_text=\"'asc', 'desc'\",\n        required=False\n    )\n\n\nclass QueueForm(forms.Form):\n    count = forms.IntegerField(\n        required=False\n    )\n    page = forms.IntegerField(\n        required=False\n    )\n\n    order_by = forms.CharField(\n        max_length=7,\n        help_text=\"'title', 'artist', 'album', 'year', \\\n            'genre', 'created', 'votes'\",\n        required=False\n    )\n    order_direction = forms.CharField(\n        max_length=4,\n        help_text=\"'asc', 'desc'\",\n        required=False\n    )\n"
  },
  {
    "path": "jukebox/jukebox_core/management/__init__.py",
    "content": ""
  },
  {
    "path": "jukebox/jukebox_core/management/commands/__init__.py",
    "content": ""
  },
  {
    "path": "jukebox/jukebox_core/management/commands/jukebox_index.py",
    "content": "# -*- coding: UTF-8 -*-\nfrom django.core.management.base import BaseCommand\nfrom optparse import make_option\nimport os\nfrom jukebox.jukebox_core.utils import FileIndexer\n\n\nclass Command(BaseCommand):\n    option_list = BaseCommand.option_list + (\n        make_option(\"--path\", action=\"store\", dest=\"path\",\n                    help=\"Music library path to scan\"),\n    )\n\n    def handle(self, *args, **options):\n        if options[\"path\"] is None:\n            print \"Required arguments: path\"\n            return\n\n        if not os.path.exists(options[\"path\"]):\n            print \"Path does not exist\"\n            return\n\n        print \"Indexing music in \" + options[\"path\"]\n        print \"This may take a while\"\n        self.index(options[\"path\"], int(options[\"verbosity\"]))\n\n    def index(self, path, verbosity):\n        if not path.endswith(\"/\"):\n            path += \"/\"\n\n        indexer = FileIndexer()\n\n        listing = os.listdir(path)\n        for filename in listing:\n            filename = path + filename\n            if os.path.isdir(filename):\n                self.index(filename + \"/\", verbosity)\n            elif filename.endswith(\".mp3\"):\n                if verbosity >= 2:\n                    print \"Indexing file \" + filename\n                indexer.index(filename)\n"
  },
  {
    "path": "jukebox/jukebox_core/management/commands/jukebox_setup.py",
    "content": "# -*- coding: UTF-8 -*-\nfrom django.core.management.base import BaseCommand\nfrom django.conf import settings\n\n\nclass Command(BaseCommand):\n    def handle(self, *args, **options):\n        print \"----------------------------------------------\"\n        print \"-----    Welcome to the jukebox setup    -----\"\n        print \"----------------------------------------------\"\n        print \"\"\n\n        print \"Page administrator\"\n        print \"----------------------------------------------\"\n        print \"\"\n        admin_user = raw_input(\"\\tName: \")\n        admin_email = raw_input(\"\\tE-mail: \")\n        print \"\"\n\n        # get authentication methods\n        authentication = self.setAuthentication()\n        while not authentication:\n            authentication = self.setAuthentication()\n\n        self.setup(admin_user, admin_email, authentication)\n\n    def setAuthentication(self):\n        print \"Please select your authentication methods\"\n        print \"----------------------------------------------\"\n        print \"Available providers: Facebook, Twitter, Github\"\n        print \"\"\n\n        print \"Facebook\"\n        print \"\\tFacebook authentication requires setup of a Facebook app on \"\\\n            \"http://developers.facebook.com/setup/\"\n        facebook = self.readAppData(\"Facebook\")\n        print \"\"\n\n        print \"Twitter\"\n        print \"\\tTwitter authentication requires setup of a Twitter app on \"\\\n            \"https://dev.twitter.com/apps/new\"\n        twitter = self.readAppData(\"Twitter\")\n        print \"\"\n\n        print \"Github\"\n        print \"\\tGithub authentication requires setup of a Github app on \"\\\n            \"https://github.com/settings/applications/new\"\n        github = self.readAppData(\"Github\")\n        print \"\"\n\n        if facebook is None and twitter is None and github is None:\n            print \"Are your kidding me? Why didn't you select a provider?\"\n            print \"I won't let you go until you select at least one of them.\"\n            print \"\"\n            return False\n\n        return {\n            \"facebook\": facebook,\n            \"twitter\": twitter,\n            \"github\": github,\n        }\n\n    def readAppData(self, name):\n        type = raw_input(\n            \"\\tUse \" + name + \" for authentication? [y/n] \"\n        ).strip().lower()\n        while type != \"n\" and type != \"y\":\n            type = raw_input(\n                \"\\tInvalid answer, please enter 'y' for yes or 'n' for no: \"\n            ).strip().lower()\n\n        if type == \"y\":\n            id = \"\"\n            while id.strip() == \"\":\n                id = raw_input(\"\\t\" + name + \" app id: \")\n                if id.strip() == \"\":\n                    print \"\\t\\tInvald app id\"\n            secret = \"\"\n            while secret.strip() == \"\":\n                secret = raw_input(\"\\t\" + name + \" app secret: \")\n                if secret.strip() == \"\":\n                    print \"\\t\\tInvald app id\"\n\n            return {\n                \"id\": id,\n                \"secret\": secret,\n            }\n\n        return None\n\n    def setup(self, admin_user, admin_email, authentication):\n        setup = open(settings.BASE_DIR + \"/settings_local.example.py\").read()\n\n        setup = setup.replace(\"[admin_user]\", admin_user)\n        setup = setup.replace(\"[admin_email]\", admin_email)\n\n        auth_backends = []\n        auth_backends_enabled = []\n\n        if authentication[\"facebook\"] is not None:\n            auth_backends.append(\n                \"\\\"social_auth.backends.facebook.FacebookBackend\\\",\"\n            )\n            auth_backends_enabled.append(\"\\\"facebook\\\",\")\n\n        if authentication[\"twitter\"] is not None:\n            auth_backends.append(\n                \"\\\"social_auth.backends.twitter.TwitterBackend\\\",\"\n            )\n            auth_backends_enabled.append(\"\\\"twitter\\\",\")\n\n        if authentication[\"github\"] is not None:\n            auth_backends.append(\n                \"\\\"social_auth.backends.contrib.github.GithubBackend\\\",\"\n            )\n            auth_backends_enabled.append(\"\\\"github\\\",\")\n\n        setup = setup.replace(\n            \"[auth_backends]\",\n            \"\\n    \".join(auth_backends)\n        )\n        setup = setup.replace(\n            \"[auth_backends_enabled]\",\n            \" \".join(auth_backends_enabled)\n        )\n\n        auth_data = \"\"\n        for key, value in authentication.items():\n            if value is None:\n                continue\n\n            if key == 'twitter':\n                auth_data += \"TWITTER_CONSUMER_KEY = \\\"\" + \\\n                     value[\"id\"] + \"\\\"\\n\"\n                auth_data += \"TWITTER_CONSUMER_SECRET = \\\"\" + \\\n                     value[\"secret\"] + \"\\\"\\n\"\n            else:\n                auth_data += key.upper() + \"_APP_ID = \\\"\" + \\\n                     value[\"id\"] + \"\\\"\\n\"\n                auth_data += key.upper() + \"_API_SECRET = \\\"\" + \\\n                     value[\"secret\"] + \"\\\"\\n\"\n            auth_data += \"\\n\"\n\n        setup = setup.replace(\"[auth_data]\", auth_data)\n\n        f = open(settings.JUKEBOX_STORAGE_PATH + \"/settings_local.py\", \"w+\")\n        f.write(setup)\n        f.close()\n\n        print \"Setup finished\"\n        print \"----------------------------------------------\"\n"
  },
  {
    "path": "jukebox/jukebox_core/migrations/0001_initial.py",
    "content": "# -*- coding: utf-8 -*-\nimport datetime\nfrom south.db import db\nfrom south.v2 import SchemaMigration\nfrom django.db import models\n\n\nclass Migration(SchemaMigration):\n\n    def forwards(self, orm):\n        # Adding model 'Artist'\n        db.create_table('jukebox_core_artist', (\n            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),\n            ('Name', self.gf('django.db.models.fields.CharField')(max_length=200)),\n        ))\n        db.send_create_signal('jukebox_core', ['Artist'])\n\n        # Adding model 'Genre'\n        db.create_table('jukebox_core_genre', (\n            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),\n            ('Name', self.gf('django.db.models.fields.CharField')(max_length=200)),\n        ))\n        db.send_create_signal('jukebox_core', ['Genre'])\n\n        # Adding model 'Album'\n        db.create_table('jukebox_core_album', (\n            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),\n            ('Title', self.gf('django.db.models.fields.CharField')(max_length=200)),\n            ('Artist', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['jukebox_core.Artist'])),\n        ))\n        db.send_create_signal('jukebox_core', ['Album'])\n\n        # Adding model 'Song'\n        db.create_table('jukebox_core_song', (\n            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),\n            ('Artist', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['jukebox_core.Artist'])),\n            ('Album', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['jukebox_core.Album'], null=True)),\n            ('Genre', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['jukebox_core.Genre'], null=True)),\n            ('Title', self.gf('django.db.models.fields.CharField')(max_length=200)),\n            ('Year', self.gf('django.db.models.fields.IntegerField')(null=True)),\n            ('Length', self.gf('django.db.models.fields.IntegerField')()),\n            ('Filename', self.gf('django.db.models.fields.CharField')(max_length=1000)),\n        ))\n        db.send_create_signal('jukebox_core', ['Song'])\n\n        # Adding model 'Queue'\n        db.create_table('jukebox_core_queue', (\n            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),\n            ('Song', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['jukebox_core.Song'], unique=True)),\n            ('Created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),\n        ))\n        db.send_create_signal('jukebox_core', ['Queue'])\n\n        # Adding M2M table for field User on 'Queue'\n        db.create_table('jukebox_core_queue_User', (\n            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),\n            ('queue', models.ForeignKey(orm['jukebox_core.queue'], null=False)),\n            ('user', models.ForeignKey(orm['auth.user'], null=False))\n        ))\n        db.create_unique('jukebox_core_queue_User', ['queue_id', 'user_id'])\n\n        # Adding model 'Favourite'\n        db.create_table('jukebox_core_favourite', (\n            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),\n            ('Song', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['jukebox_core.Song'])),\n            ('User', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),\n            ('Created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),\n        ))\n        db.send_create_signal('jukebox_core', ['Favourite'])\n\n        # Adding unique constraint on 'Favourite', fields ['Song', 'User']\n        db.create_unique('jukebox_core_favourite', ['Song_id', 'User_id'])\n\n        # Adding model 'History'\n        db.create_table('jukebox_core_history', (\n            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),\n            ('Song', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['jukebox_core.Song'])),\n            ('Created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),\n        ))\n        db.send_create_signal('jukebox_core', ['History'])\n\n        # Adding M2M table for field User on 'History'\n        db.create_table('jukebox_core_history_User', (\n            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),\n            ('history', models.ForeignKey(orm['jukebox_core.history'], null=False)),\n            ('user', models.ForeignKey(orm['auth.user'], null=False))\n        ))\n        db.create_unique('jukebox_core_history_User', ['history_id', 'user_id'])\n\n        # Adding model 'Player'\n        db.create_table('jukebox_core_player', (\n            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),\n            ('Pid', self.gf('django.db.models.fields.IntegerField')()),\n        ))\n        db.send_create_signal('jukebox_core', ['Player'])\n\n\n    def backwards(self, orm):\n        # Removing unique constraint on 'Favourite', fields ['Song', 'User']\n        db.delete_unique('jukebox_core_favourite', ['Song_id', 'User_id'])\n\n        # Deleting model 'Artist'\n        db.delete_table('jukebox_core_artist')\n\n        # Deleting model 'Genre'\n        db.delete_table('jukebox_core_genre')\n\n        # Deleting model 'Album'\n        db.delete_table('jukebox_core_album')\n\n        # Deleting model 'Song'\n        db.delete_table('jukebox_core_song')\n\n        # Deleting model 'Queue'\n        db.delete_table('jukebox_core_queue')\n\n        # Removing M2M table for field User on 'Queue'\n        db.delete_table('jukebox_core_queue_User')\n\n        # Deleting model 'Favourite'\n        db.delete_table('jukebox_core_favourite')\n\n        # Deleting model 'History'\n        db.delete_table('jukebox_core_history')\n\n        # Removing M2M table for field User on 'History'\n        db.delete_table('jukebox_core_history_User')\n\n        # Deleting model 'Player'\n        db.delete_table('jukebox_core_player')\n\n\n    models = {\n        'auth.group': {\n            'Meta': {'object_name': 'Group'},\n            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),\n            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),\n            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': \"orm['auth.Permission']\", 'symmetrical': 'False', 'blank': 'True'})\n        },\n        'auth.permission': {\n            'Meta': {'ordering': \"('content_type__app_label', 'content_type__model', 'codename')\", 'unique_together': \"(('content_type', 'codename'),)\", 'object_name': 'Permission'},\n            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),\n            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': \"orm['contenttypes.ContentType']\"}),\n            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),\n            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})\n        },\n        'auth.user': {\n            'Meta': {'object_name': 'User'},\n            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),\n            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),\n            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),\n            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': \"orm['auth.Group']\", 'symmetrical': 'False', 'blank': 'True'}),\n            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),\n            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),\n            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),\n            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),\n            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),\n            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),\n            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),\n            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': \"orm['auth.Permission']\", 'symmetrical': 'False', 'blank': 'True'}),\n            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})\n        },\n        'contenttypes.contenttype': {\n            'Meta': {'ordering': \"('name',)\", 'unique_together': \"(('app_label', 'model'),)\", 'object_name': 'ContentType', 'db_table': \"'django_content_type'\"},\n            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),\n            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),\n            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),\n            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})\n        },\n        'jukebox_core.album': {\n            'Artist': ('django.db.models.fields.related.ForeignKey', [], {'to': \"orm['jukebox_core.Artist']\"}),\n            'Meta': {'ordering': \"['Title']\", 'object_name': 'Album'},\n            'Title': ('django.db.models.fields.CharField', [], {'max_length': '200'}),\n            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})\n        },\n        'jukebox_core.artist': {\n            'Meta': {'ordering': \"['Name']\", 'object_name': 'Artist'},\n            'Name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),\n            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})\n        },\n        'jukebox_core.favourite': {\n            'Created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),\n            'Meta': {'ordering': \"['-Created']\", 'unique_together': \"(('Song', 'User'),)\", 'object_name': 'Favourite'},\n            'Song': ('django.db.models.fields.related.ForeignKey', [], {'to': \"orm['jukebox_core.Song']\"}),\n            'User': ('django.db.models.fields.related.ForeignKey', [], {'to': \"orm['auth.User']\"}),\n            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})\n        },\n        'jukebox_core.genre': {\n            'Meta': {'ordering': \"['Name']\", 'object_name': 'Genre'},\n            'Name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),\n            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})\n        },\n        'jukebox_core.history': {\n            'Created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),\n            'Meta': {'ordering': \"['-Created']\", 'object_name': 'History'},\n            'Song': ('django.db.models.fields.related.ForeignKey', [], {'to': \"orm['jukebox_core.Song']\"}),\n            'User': ('django.db.models.fields.related.ManyToManyField', [], {'to': \"orm['auth.User']\", 'null': 'True', 'symmetrical': 'False'}),\n            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})\n        },\n        'jukebox_core.player': {\n            'Meta': {'object_name': 'Player'},\n            'Pid': ('django.db.models.fields.IntegerField', [], {}),\n            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})\n        },\n        'jukebox_core.queue': {\n            'Created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),\n            'Meta': {'object_name': 'Queue'},\n            'Song': ('django.db.models.fields.related.ForeignKey', [], {'to': \"orm['jukebox_core.Song']\", 'unique': 'True'}),\n            'User': ('django.db.models.fields.related.ManyToManyField', [], {'to': \"orm['auth.User']\", 'symmetrical': 'False'}),\n            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})\n        },\n        'jukebox_core.song': {\n            'Album': ('django.db.models.fields.related.ForeignKey', [], {'to': \"orm['jukebox_core.Album']\", 'null': 'True'}),\n            'Artist': ('django.db.models.fields.related.ForeignKey', [], {'to': \"orm['jukebox_core.Artist']\"}),\n            'Filename': ('django.db.models.fields.CharField', [], {'max_length': '1000'}),\n            'Genre': ('django.db.models.fields.related.ForeignKey', [], {'to': \"orm['jukebox_core.Genre']\", 'null': 'True'}),\n            'Length': ('django.db.models.fields.IntegerField', [], {}),\n            'Meta': {'ordering': \"['Title', 'Artist', 'Album']\", 'object_name': 'Song'},\n            'Title': ('django.db.models.fields.CharField', [], {'max_length': '200'}),\n            'Year': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),\n            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})\n        }\n    }\n\n    complete_apps = ['jukebox_core']"
  },
  {
    "path": "jukebox/jukebox_core/migrations/0002_auto__del_field_album_Artist.py",
    "content": "# -*- coding: utf-8 -*-\nimport datetime\nfrom south.db import db\nfrom south.v2 import SchemaMigration\nfrom django.db import models\n\n\nclass Migration(SchemaMigration):\n\n    def forwards(self, orm):\n        # Deleting field 'Album.Artist'\n        db.delete_column('jukebox_core_album', 'Artist_id')\n\n\n    def backwards(self, orm):\n\n        # User chose to not deal with backwards NULL issues for 'Album.Artist'\n        raise RuntimeError(\"Cannot reverse this migration. 'Album.Artist' and its values cannot be restored.\")\n\n    models = {\n        'auth.group': {\n            'Meta': {'object_name': 'Group'},\n            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),\n            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),\n            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': \"orm['auth.Permission']\", 'symmetrical': 'False', 'blank': 'True'})\n        },\n        'auth.permission': {\n            'Meta': {'ordering': \"('content_type__app_label', 'content_type__model', 'codename')\", 'unique_together': \"(('content_type', 'codename'),)\", 'object_name': 'Permission'},\n            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),\n            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': \"orm['contenttypes.ContentType']\"}),\n            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),\n            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})\n        },\n        'auth.user': {\n            'Meta': {'object_name': 'User'},\n            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),\n            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),\n            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),\n            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': \"orm['auth.Group']\", 'symmetrical': 'False', 'blank': 'True'}),\n            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),\n            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),\n            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),\n            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),\n            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),\n            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),\n            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),\n            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': \"orm['auth.Permission']\", 'symmetrical': 'False', 'blank': 'True'}),\n            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})\n        },\n        'contenttypes.contenttype': {\n            'Meta': {'ordering': \"('name',)\", 'unique_together': \"(('app_label', 'model'),)\", 'object_name': 'ContentType', 'db_table': \"'django_content_type'\"},\n            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),\n            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),\n            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),\n            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})\n        },\n        'jukebox_core.album': {\n            'Meta': {'ordering': \"['Title']\", 'object_name': 'Album'},\n            'Title': ('django.db.models.fields.CharField', [], {'max_length': '200'}),\n            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})\n        },\n        'jukebox_core.artist': {\n            'Meta': {'ordering': \"['Name']\", 'object_name': 'Artist'},\n            'Name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),\n            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})\n        },\n        'jukebox_core.favourite': {\n            'Created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),\n            'Meta': {'ordering': \"['-Created']\", 'unique_together': \"(('Song', 'User'),)\", 'object_name': 'Favourite'},\n            'Song': ('django.db.models.fields.related.ForeignKey', [], {'to': \"orm['jukebox_core.Song']\"}),\n            'User': ('django.db.models.fields.related.ForeignKey', [], {'to': \"orm['auth.User']\"}),\n            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})\n        },\n        'jukebox_core.genre': {\n            'Meta': {'ordering': \"['Name']\", 'object_name': 'Genre'},\n            'Name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),\n            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})\n        },\n        'jukebox_core.history': {\n            'Created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),\n            'Meta': {'ordering': \"['-Created']\", 'object_name': 'History'},\n            'Song': ('django.db.models.fields.related.ForeignKey', [], {'to': \"orm['jukebox_core.Song']\"}),\n            'User': ('django.db.models.fields.related.ManyToManyField', [], {'to': \"orm['auth.User']\", 'null': 'True', 'symmetrical': 'False'}),\n            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})\n        },\n        'jukebox_core.player': {\n            'Meta': {'object_name': 'Player'},\n            'Pid': ('django.db.models.fields.IntegerField', [], {}),\n            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})\n        },\n        'jukebox_core.queue': {\n            'Created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),\n            'Meta': {'object_name': 'Queue'},\n            'Song': ('django.db.models.fields.related.ForeignKey', [], {'to': \"orm['jukebox_core.Song']\", 'unique': 'True'}),\n            'User': ('django.db.models.fields.related.ManyToManyField', [], {'to': \"orm['auth.User']\", 'symmetrical': 'False'}),\n            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})\n        },\n        'jukebox_core.song': {\n            'Album': ('django.db.models.fields.related.ForeignKey', [], {'to': \"orm['jukebox_core.Album']\", 'null': 'True'}),\n            'Artist': ('django.db.models.fields.related.ForeignKey', [], {'to': \"orm['jukebox_core.Artist']\"}),\n            'Filename': ('django.db.models.fields.CharField', [], {'max_length': '1000'}),\n            'Genre': ('django.db.models.fields.related.ForeignKey', [], {'to': \"orm['jukebox_core.Genre']\", 'null': 'True'}),\n            'Length': ('django.db.models.fields.IntegerField', [], {}),\n            'Meta': {'ordering': \"['Title', 'Artist', 'Album']\", 'object_name': 'Song'},\n            'Title': ('django.db.models.fields.CharField', [], {'max_length': '200'}),\n            'Year': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),\n            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})\n        }\n    }\n\n    complete_apps = ['jukebox_core']"
  },
  {
    "path": "jukebox/jukebox_core/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "jukebox/jukebox_core/models.py",
    "content": "# -*- coding: UTF-8 -*-\n\nfrom django.db import models\nfrom django.contrib.auth.models import User\nfrom django.contrib.syndication.views import Feed\nimport time\n\nclass Artist(models.Model):\n    class Meta:\n        ordering = ['Name']\n\n    def __unicode__(self):\n        return \"%s\" % self.Name\n\n    Name = models.CharField(max_length=200)\n\n\nclass Genre(models.Model):\n    class Meta:\n        ordering = ['Name']\n\n    def __unicode__(self):\n        return \"%s\" % self.Name\n\n    Name = models.CharField(max_length=200)\n\n\nclass Album(models.Model):\n    class Meta:\n        ordering = ['Title']\n\n    def __unicode__(self):\n        return \"%s\" % self.Title\n\n    Title = models.CharField(max_length=200)\n\n\nclass Song(models.Model):\n    class Meta:\n        ordering = ['Title', 'Artist', 'Album']\n\n    def __unicode__(self):\n        return \"%s - %s\" % (self.Artist.Name, self.Title)\n\n    Artist = models.ForeignKey(Artist)\n    Album = models.ForeignKey(Album, null=True)\n    Genre = models.ForeignKey(Genre, null=True)\n    Title = models.CharField(max_length=200)\n    Year = models.IntegerField(null=True)\n    Length = models.IntegerField()\n    Filename = models.CharField(max_length=1000)\n\n\nclass Queue(models.Model):\n    Song = models.ForeignKey(Song, unique=True)\n    User = models.ManyToManyField(User)\n    Created = models.DateTimeField(auto_now_add=True)\n\n\nclass Favourite(models.Model):\n    class Meta:\n        unique_together = (\"Song\", \"User\")\n        ordering = ['-Created']\n\n    Song = models.ForeignKey(Song)\n    User = models.ForeignKey(User)\n    Created = models.DateTimeField(auto_now_add=True)\n\n\nclass History(models.Model):\n    class Meta:\n        ordering = ['-Created']\n\n    Song = models.ForeignKey(Song)\n    User = models.ManyToManyField(User, null=True)\n    Created = models.DateTimeField(auto_now_add=True)\n\n\nclass Player(models.Model):\n    Pid = models.IntegerField()\n\n\nclass QueueFeed(Feed):\n    title = \"Jukebox Queue Feed\"\n    link = \"/queue/\"\n    description = \"Top song in the queue\"\n\n    def items(self):\n        return Queue.objects.all()[:1]\n\n    def item_title(self, item):\n        return item.Song.Title\n\n\n    def item_description(self, item):\n        return unicode(item.Song.Title) + \" by \" + \\\n                unicode(item.Song.Artist) + \" from \" + \\\n                unicode(item.Song.Album)\n\n\n    def item_link(self, item):\n        # Not sure what to do with url as there isn't any unque url for song\n        return \"/queue/#\" + unicode(int(round(time.time() * 1000)))\n\n"
  },
  {
    "path": "jukebox/jukebox_core/tests/__init__.py",
    "content": "# -*- coding: UTF-8 -*-\n\nfrom .api_songs import *\nfrom .api_artists import *\nfrom .api_albums import *\nfrom .api_genres import *\nfrom .api_years import *\nfrom .api_favourites import *\nfrom .api_history import *\nfrom .api_queue import *\n"
  },
  {
    "path": "jukebox/jukebox_core/tests/api.py",
    "content": "# -*- coding: UTF-8 -*-\n\nfrom django.test import TestCase, Client\nfrom django.db import transaction\nimport base64\nfrom jukebox.jukebox_core.models import Artist, Album, Genre, Song\nfrom django.contrib.auth.models import User\n\n\nclass ApiTestBase(TestCase):\n    user = None\n    username = \"TestUser\"\n    email = \"test@domain.org\"\n    password = \"TestPassword\"\n    passwords = {}\n\n    def setUp(self):\n        transaction.rollback()\n\n        # register test user and setup auth\n        self.user = self.addUser(self.username, self.email, self.password)\n\n    def httpGet(self, url, params={}, user=None):\n        c = Client()\n        return c.get(url, params, HTTP_AUTHORIZATION=self.getAuth(user))\n\n    def httpPost(self, url, params={}, user=None):\n        c = Client()\n        return c.post(url, params, HTTP_AUTHORIZATION=self.getAuth(user))\n\n    def httpDelete(self, url, params={}, user=None):\n        c = Client()\n        return c.delete(url, params, HTTP_AUTHORIZATION=self.getAuth(user))\n\n    def getAuth(self, user=None):\n        if user is None:\n            user = self.user\n        username = user.username\n        password = self.passwords[user.id]\n\n        return \"Basic %s\" % base64.encodestring(\n            '%s:%s' % (username, password)\n        ).strip()\n\n\n    def addArtist(self, name=\"TestArist\"):\n        artist = Artist(\n            Name=name\n        )\n        artist.save()\n        return artist\n\n    def addAlbum(self, title=\"TestTitle\"):\n        album = Album(\n            Title=title\n        )\n        album.save()\n        return album\n\n    def addGenre(self, name=\"TestGenre\"):\n        genre = Genre(\n            Name=name\n        )\n        genre.save()\n        return genre\n\n    def addSong(\n        self,\n        artist,\n        album = None,\n        genre = None,\n        title=\"TestTitle\",\n        year=2000,\n        length=100,\n        filename=\"/path/to/test.mp3\"\n    ):\n        # save a song\n        song = Song(\n            Artist=artist,\n            Album=album,\n            Genre=genre,\n            Title=title,\n            Year=year,\n            Length=length,\n            Filename=filename\n        )\n        song.save()\n        return song\n\n    def addUser(self, username, email, password):\n        user = User.objects.create_user(username, email, password)\n        self.passwords[user.id] = password\n        return user\n"
  },
  {
    "path": "jukebox/jukebox_core/tests/api_albums.py",
    "content": "# -*- coding: UTF-8 -*-\n\nimport simplejson\nfrom jukebox.jukebox_core.tests.api import ApiTestBase\n\n\nclass ApiAlbumsTest(ApiTestBase):\n    def testIndexEmpty(self):\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/albums\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 0)\n\n    def testIndex(self):\n        album = self.addAlbum()\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/albums\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], album.id)\n\n    def testIndexOrderByAlbum(self):\n        album_a = self.addAlbum(title=\"A Title\")\n        album_b = self.addAlbum(title=\"B Title\")\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/albums?order_by=album\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], album_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], album_b.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/albums?order_by=album&order_direction=desc\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], album_b.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], album_a.id)\n\n    def testCount(self):\n        album_a = self.addAlbum(\"AAA\")\n        album_b = self.addAlbum(\"BBB\")\n        album_c = self.addAlbum(\"CCC\")\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/albums?count=1\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], album_a.id)\n        self.assertTrue(result[\"hasNextPage\"])\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/albums?count=3\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 3)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], album_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], album_b.id)\n        self.assertEquals(result[\"itemList\"][2][\"id\"], album_c.id)\n        self.assertFalse(result[\"hasNextPage\"])\n\n    def testCountAndPage(self):\n        album_a = self.addAlbum(\"AAA\")\n        album_b = self.addAlbum(\"BBB\")\n        album_c = self.addAlbum(\"CCC\")\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/albums?count=1&page=1\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], album_a.id)\n        self.assertTrue(result[\"hasNextPage\"])\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/albums?count=1&page=2\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], album_b.id)\n        self.assertTrue(result[\"hasNextPage\"])\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/albums?count=1&page=3\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], album_c.id)\n        self.assertFalse(result[\"hasNextPage\"])\n"
  },
  {
    "path": "jukebox/jukebox_core/tests/api_artists.py",
    "content": "# -*- coding: UTF-8 -*-\n\nimport simplejson\nfrom jukebox.jukebox_core.tests.api import ApiTestBase\n\n\nclass ApiArtistsTest(ApiTestBase):\n    def testIndexEmpty(self):\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/artists\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 0)\n\n    def testIndex(self):\n        artist = self.addArtist()\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/artists\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], artist.id)\n\n    def testIndexOrderBy(self):\n        artist_a = self.addArtist(name=\"A Name\")\n        artist_b = self.addArtist(name=\"B Name\")\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/artists?order_by=artist\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], artist_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], artist_b.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/artists?order_by=artist&order_direction=desc\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], artist_b.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], artist_a.id)\n\n    def testCount(self):\n        artist_a = self.addArtist()\n        artist_b = self.addArtist()\n        artist_c = self.addArtist()\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/artists?count=1\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], artist_a.id)\n        self.assertTrue(result[\"hasNextPage\"])\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/artists?count=3\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 3)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], artist_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], artist_b.id)\n        self.assertEquals(result[\"itemList\"][2][\"id\"], artist_c.id)\n        self.assertFalse(result[\"hasNextPage\"])\n\n    def testCountAndPage(self):\n        artist_a = self.addArtist()\n        artist_b = self.addArtist()\n        artist_c = self.addArtist()\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/artists?count=1&page=1\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], artist_a.id)\n        self.assertTrue(result[\"hasNextPage\"])\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/artists?count=1&page=2\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], artist_b.id)\n        self.assertTrue(result[\"hasNextPage\"])\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/artists?count=1&page=3\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], artist_c.id)\n        self.assertFalse(result[\"hasNextPage\"])\n"
  },
  {
    "path": "jukebox/jukebox_core/tests/api_favourites.py",
    "content": "# -*- coding: UTF-8 -*-\n\nimport simplejson\nfrom jukebox.jukebox_core.tests.api import ApiTestBase\n\n# ATTENTION: order tests\n# favourites are ordered by insertion date DESC per default\nclass ApiFavouritesTest(ApiTestBase):\n    def testIndexEmpty(self):\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/favourites\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 0)\n\n    def testAddAndIndex(self):\n        # register second user\n        user = self.addUser(\"TestUser2\", \"test2@domain.org\", \"TestPassword2\")\n\n        song = self.addSong(artist=self.addArtist())\n\n        # check that song is not a favourite\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/songs\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song.id)\n        self.assertFalse(result[\"itemList\"][0][\"favourite\"])\n\n        # add to favourites\n        response = self.httpPost(\n            \"/api/v1/favourites\",\n            {\"id\": song.id}\n        )\n        content = simplejson.loads(\n            response.content\n        )\n        self.assertEqual(response.status_code, 201)\n        self.assertEqual(content[\"id\"], song.id)\n\n        # check favourites list\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/favourites\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song.id)\n\n        # check that song is marked as favourite\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/songs\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song.id)\n        self.assertTrue(result[\"itemList\"][0][\"favourite\"])\n\n        # check favourites list of second user\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/favourites\",\n                {},\n                user\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 0)\n\n        # check that song is not marked as favourite for second user\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/songs\",\n                {},\n                user\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song.id)\n        self.assertFalse(result[\"itemList\"][0][\"favourite\"])\n\n    def testDeleteAndIndex(self):\n        song = self.addSong(artist=self.addArtist())\n\n        # add to favourites\n        response = self.httpPost(\n            \"/api/v1/favourites\",\n            {\"id\": song.id}\n        )\n        content = simplejson.loads(\n            response.content\n        )\n        self.assertEqual(response.status_code, 201)\n        self.assertEqual(content[\"id\"], song.id)\n\n        # check favourites list\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/favourites\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song.id)\n\n        # remove from favourites\n        response = self.httpDelete(\n            \"/api/v1/favourites/\" + str(song.id),\n        )\n        content = simplejson.loads(\n            response.content\n        )\n        self.assertEqual(response.status_code, 200)\n        self.assertEqual(content[\"id\"], str(song.id))\n\n        # check favourites list\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/favourites\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 0)\n\n    def addFavourite(self, song):\n        return self.httpPost(\n            \"/api/v1/favourites\",\n            {\"id\": song.id}\n        )\n\n    def testIndexOrderByTitle(self):\n        song_a = self.addSong(artist=self.addArtist(), title=\"A Title\")\n        song_b = self.addSong(artist=self.addArtist(), title=\"B Title\")\n        self.addFavourite(song_a)\n        self.addFavourite(song_b)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/favourites?order_by=title\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_b.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/favourites?order_by=title&order_direction=desc\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_b.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_a.id)\n\n    def testIndexOrderByArtist(self):\n        song_a = self.addSong(artist=self.addArtist(name=\"A Name\"))\n        song_b = self.addSong(artist=self.addArtist(name=\"B Name\"))\n        self.addFavourite(song_a)\n        self.addFavourite(song_b)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/favourites?order_by=artist\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_b.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/favourites?order_by=artist&order_direction=desc\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_b.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_a.id)\n\n    def testIndexOrderByAlbum(self):\n        album_a = self.addAlbum(\"A Title\")\n        album_b = self.addAlbum(\"B Title\")\n        song_a = self.addSong(artist=self.addArtist(), album=album_a)\n        song_b = self.addSong(artist=self.addArtist(), album=album_b)\n        self.addFavourite(song_a)\n        self.addFavourite(song_b)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/favourites?order_by=album\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_b.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/favourites?order_by=album&order_direction=desc\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_b.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_a.id)\n\n    def testIndexOrderByYear(self):\n        song_a = self.addSong(artist=self.addArtist(), year=2000)\n        song_b = self.addSong(artist=self.addArtist(), year=2001)\n        self.addFavourite(song_a)\n        self.addFavourite(song_b)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/favourites?order_by=year\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_b.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/favourites?order_by=year&order_direction=desc\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_b.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_a.id)\n\n    def testIndexOrderByGenre(self):\n        song_a = self.addSong(\n            artist=self.addArtist(),\n            genre=self.addGenre(name=\"A Genre\")\n        )\n        song_b = self.addSong(\n            artist=self.addArtist(),\n            genre=self.addGenre(name=\"B Genre\")\n        )\n        self.addFavourite(song_a)\n        self.addFavourite(song_b)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/favourites?order_by=genre\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_b.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/favourites?order_by=genre&order_direction=desc\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_b.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_a.id)\n\n    def testIndexOrderByCreated(self):\n        song_a = self.addSong(artist=self.addArtist())\n        song_b = self.addSong(artist=self.addArtist())\n        self.addFavourite(song_a)\n        self.addFavourite(song_b)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/favourites?order_by=created\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_b.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/favourites?order_by=created&order_direction=desc\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_b.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_a.id)\n\n    def testCount(self):\n        song_a = self.addSong(artist=self.addArtist())\n        song_b = self.addSong(artist=self.addArtist())\n        song_c = self.addSong(artist=self.addArtist())\n        self.addFavourite(song_a)\n        self.addFavourite(song_b)\n        self.addFavourite(song_c)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/favourites?count=1\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_c.id)\n        self.assertTrue(result[\"hasNextPage\"])\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/favourites?count=3\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 3)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_c.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_b.id)\n        self.assertEquals(result[\"itemList\"][2][\"id\"], song_a.id)\n        self.assertFalse(result[\"hasNextPage\"])\n\n    def testCountAndPage(self):\n        song_a = self.addSong(artist=self.addArtist())\n        song_b = self.addSong(artist=self.addArtist())\n        song_c = self.addSong(artist=self.addArtist())\n        self.addFavourite(song_a)\n        self.addFavourite(song_b)\n        self.addFavourite(song_c)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/favourites?count=1&page=1\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_c.id)\n        self.assertTrue(result[\"hasNextPage\"])\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/favourites?count=1&page=2\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_b.id)\n        self.assertTrue(result[\"hasNextPage\"])\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/favourites?count=1&page=3\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertFalse(result[\"hasNextPage\"])\n"
  },
  {
    "path": "jukebox/jukebox_core/tests/api_genres.py",
    "content": "# -*- coding: UTF-8 -*-\n\nimport simplejson\nfrom jukebox.jukebox_core.tests.api import ApiTestBase\n\n\nclass ApiGenresTest(ApiTestBase):\n    def testIndexEmpty(self):\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/genres\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 0)\n\n    def testIndex(self):\n        genre = self.addGenre()\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/genres\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], genre.id)\n\n    def testIndexOrderBy(self):\n        genre_a = self.addGenre(name=\"A Name\")\n        genre_b = self.addGenre(name=\"B Name\")\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/genres?order_by=genre\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], genre_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], genre_b.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/genres?order_by=genre&order_direction=desc\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], genre_b.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], genre_a.id)\n\n    def testCount(self):\n        genre_a = self.addGenre()\n        genre_b = self.addGenre()\n        genre_c = self.addGenre()\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/genres?count=1\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], genre_a.id)\n        self.assertTrue(result[\"hasNextPage\"])\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/genres?count=3\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 3)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], genre_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], genre_b.id)\n        self.assertEquals(result[\"itemList\"][2][\"id\"], genre_c.id)\n        self.assertFalse(result[\"hasNextPage\"])\n\n    def testCountAndPage(self):\n        genre_a = self.addGenre()\n        genre_b = self.addGenre()\n        genre_c = self.addGenre()\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/genres?count=1&page=1\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], genre_a.id)\n        self.assertTrue(result[\"hasNextPage\"])\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/genres?count=1&page=2\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], genre_b.id)\n        self.assertTrue(result[\"hasNextPage\"])\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/genres?count=1&page=3\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], genre_c.id)\n        self.assertFalse(result[\"hasNextPage\"])\n"
  },
  {
    "path": "jukebox/jukebox_core/tests/api_history.py",
    "content": "# -*- coding: UTF-8 -*-\n\nimport simplejson\nfrom jukebox.jukebox_core import api\nfrom jukebox.jukebox_core.tests.api import ApiTestBase\n\n# ATTENTION: order tests\n# favourites are ordered by insertion date DESC per default\nclass ApiHistoryTest(ApiTestBase):\n    def testIndexEmpty(self):\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 0)\n\n    def addSongToQueue(self, song, user=None):\n        if user is None:\n            user = self.user\n\n        return self.httpPost(\n            \"/api/v1/queue\",\n            {\"id\": song.id},\n            user\n        )\n\n    def getNextSong(self):\n        songs_api = api.songs()\n        return songs_api.getNextSong()\n\n    def testAddAndIndex(self):\n        song = self.addSong(artist=self.addArtist(), filename= __file__)\n\n        # check that song is not in history\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 0)\n\n        # add to queue and play the song\n        self.addSongToQueue(song)\n        self.getNextSong()\n\n        # check history\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song.id)\n\n    def testAddAndIndexMy(self):\n        # register second user\n        user = self.addUser(\"TestUser2\", \"test2@domain.org\", \"TestPassword2\")\n\n        song_a = self.addSong(artist=self.addArtist(), filename=__file__)\n        song_b = self.addSong(artist=self.addArtist(), filename=__file__)\n\n        # check that song is not in history\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history/my\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 0)\n\n        # add to queue and play the song\n        self.addSongToQueue(song_a, user)\n        self.addSongToQueue(song_b)\n        self.getNextSong()\n\n        # overall history contains the song\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n\n        # my history should still be empty\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history/my\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 0)\n\n        # play my song\n        self.getNextSong()\n\n        # overall history contains both songs\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_b.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_a.id)\n\n        # check my history\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history/my\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_b.id)\n\n    def testIndexOrderByTitle(self):\n        song_a = self.addSong(\n            artist=self.addArtist(), title=\"A Title\", filename=__file__\n        )\n        song_b = self.addSong(\n            artist=self.addArtist(), title=\"B Title\", filename=__file__\n        )\n        self.addSongToQueue(song_a)\n        self.addSongToQueue(song_b)\n        self.getNextSong()\n        self.getNextSong()\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history?order_by=title\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_b.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history/my?order_by=title\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_b.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history?order_by=title&order_direction=desc\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_b.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_a.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history/my?order_by=title&order_direction=desc\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_b.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_a.id)\n\n    def testIndexOrderByArtist(self):\n        song_a = self.addSong(\n            artist=self.addArtist(\"A Name\"), filename=__file__\n        )\n        song_b = self.addSong(\n            artist=self.addArtist(\"B Name\"), filename=__file__\n        )\n        self.addSongToQueue(song_a)\n        self.addSongToQueue(song_b)\n        self.getNextSong()\n        self.getNextSong()\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history?order_by=artist\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_b.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history/my?order_by=artist\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_b.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history?order_by=artist&order_direction=desc\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_b.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_a.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history/my?order_by=artist&order_direction=desc\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_b.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_a.id)\n\n    def testIndexOrderByAlbum(self):\n        artist = self.addArtist()\n        song_a = self.addSong(\n            artist=artist,\n            album=self.addAlbum(title=\"A Title\"),\n            filename=__file__\n        )\n        song_b = self.addSong(\n            artist=artist,\n            album=self.addAlbum(title=\"B Title\"),\n            filename=__file__\n        )\n        self.addSongToQueue(song_a)\n        self.addSongToQueue(song_b)\n        self.getNextSong()\n        self.getNextSong()\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history?order_by=album\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_b.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history/my?order_by=album\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_b.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history?order_by=album&order_direction=desc\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_b.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_a.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history/my?order_by=album&order_direction=desc\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_b.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_a.id)\n\n    def testIndexOrderByYear(self):\n        song_a = self.addSong(\n            artist=self.addArtist(), filename=__file__\n        )\n        song_b = self.addSong(\n            artist=self.addArtist(), year=2010, filename=__file__\n        )\n        self.addSongToQueue(song_a)\n        self.addSongToQueue(song_b)\n        self.getNextSong()\n        self.getNextSong()\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history?order_by=year\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_b.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history/my?order_by=year\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_b.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history?order_by=year&order_direction=desc\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_b.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_a.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history/my?order_by=year&order_direction=desc\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_b.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_a.id)\n\n    def testIndexOrderByGenre(self):\n        song_a = self.addSong(\n            artist=self.addArtist(),\n            genre=self.addGenre(name=\"A Name\"),\n            filename=__file__\n        )\n        song_b = self.addSong(\n            artist=self.addArtist(),\n            genre=self.addGenre(name=\"B Name\"),\n            filename=__file__\n        )\n        self.addSongToQueue(song_a)\n        self.addSongToQueue(song_b)\n        self.getNextSong()\n        self.getNextSong()\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history?order_by=genre\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_b.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history/my?order_by=genre\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_b.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history?order_by=genre&order_direction=desc\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_b.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_a.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history/my?order_by=genre&order_direction=desc\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_b.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_a.id)\n\n    def testIndexOrderByCreated(self):\n        song_a = self.addSong(\n            artist=self.addArtist(),\n            filename=__file__\n        )\n        song_b = self.addSong(\n            artist=self.addArtist(),\n            filename=__file__\n        )\n        self.addSongToQueue(song_a)\n        self.addSongToQueue(song_b)\n        self.getNextSong()\n        self.getNextSong()\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history?order_by=created\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_b.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history/my?order_by=created\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_b.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history?order_by=created&order_direction=desc\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_b.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_a.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history/my?order_by=created&order_direction=desc\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_b.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_a.id)\n\n    def testCount(self):\n        song_a = self.addSong(\n            artist=self.addArtist(),\n            filename=__file__\n        )\n        song_b = self.addSong(\n            artist=self.addArtist(),\n            filename=__file__\n        )\n        song_c = self.addSong(\n            artist=self.addArtist(),\n            filename=__file__\n        )\n        self.addSongToQueue(song_a)\n        self.addSongToQueue(song_b)\n        self.addSongToQueue(song_c)\n        self.getNextSong()\n        self.getNextSong()\n        self.getNextSong()\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history?count=1\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_c.id)\n        self.assertTrue(result[\"hasNextPage\"])\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history/my?count=1\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_c.id)\n        self.assertTrue(result[\"hasNextPage\"])\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history?count=3\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 3)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_c.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_b.id)\n        self.assertEquals(result[\"itemList\"][2][\"id\"], song_a.id)\n        self.assertFalse(result[\"hasNextPage\"])\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history/my?count=3\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 3)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_c.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_b.id)\n        self.assertEquals(result[\"itemList\"][2][\"id\"], song_a.id)\n        self.assertFalse(result[\"hasNextPage\"])\n\n    def testCountAndPage(self):\n        song_a = self.addSong(\n            artist=self.addArtist(),\n            filename=__file__\n        )\n        song_b = self.addSong(\n            artist=self.addArtist(),\n            filename=__file__\n        )\n        song_c = self.addSong(\n            artist=self.addArtist(),\n            filename=__file__\n        )\n        self.addSongToQueue(song_a)\n        self.addSongToQueue(song_b)\n        self.addSongToQueue(song_c)\n        self.getNextSong()\n        self.getNextSong()\n        self.getNextSong()\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history?count=1&page=1\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_c.id)\n        self.assertTrue(result[\"hasNextPage\"])\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history?count=1&page=2\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_b.id)\n        self.assertTrue(result[\"hasNextPage\"])\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history?count=1&page=3\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertFalse(result[\"hasNextPage\"])\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history/my?count=1&page=1\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_c.id)\n        self.assertTrue(result[\"hasNextPage\"])\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history/my?count=1&page=2\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_b.id)\n        self.assertTrue(result[\"hasNextPage\"])\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/history/my?count=1&page=3\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertFalse(result[\"hasNextPage\"])\n"
  },
  {
    "path": "jukebox/jukebox_core/tests/api_queue.py",
    "content": "# -*- coding: UTF-8 -*-\n\nimport simplejson\nfrom jukebox.jukebox_core.tests.api import ApiTestBase\n\nclass ApiQueueTest(ApiTestBase):\n    def testIndexEmpty(self):\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/queue\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 0)\n\n    def testAddAndIndex(self):\n        song = self.addSong(artist=self.addArtist())\n\n        # check that song is not in queue\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/songs\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song.id)\n        self.assertFalse(result[\"itemList\"][0][\"queued\"])\n\n        # add to queue\n        response = self.httpPost(\n            \"/api/v1/queue\",\n            {\"id\": song.id}\n        )\n        content = simplejson.loads(\n            response.content\n        )\n        self.assertEqual(response.status_code, 201)\n        self.assertEqual(content[\"id\"], song.id)\n\n        # check queue\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/queue\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song.id)\n\n        # check that song is marked as queued\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/songs\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song.id)\n        self.assertTrue(result[\"itemList\"][0][\"queued\"])\n\n    def testDeleteAndIndex(self):\n        song = self.addSong(artist=self.addArtist())\n\n        # add to queue\n        response = self.httpPost(\n            \"/api/v1/queue\",\n            {\"id\": song.id}\n        )\n        content = simplejson.loads(\n            response.content\n        )\n        self.assertEqual(response.status_code, 201)\n        self.assertEqual(content[\"id\"], song.id)\n\n        # check queue\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/queue\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song.id)\n\n        # remove from queue\n        response = self.httpDelete(\n            \"/api/v1/queue/\" + str(song.id),\n        )\n        content = simplejson.loads(\n            response.content\n        )\n        self.assertEqual(response.status_code, 200)\n        self.assertEqual(content[\"id\"], str(song.id))\n\n        # check queue\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/queue\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 0)\n\n    def addToQueue(self, song):\n        return self.httpPost(\n            \"/api/v1/queue\",\n            {\"id\": song.id}\n        )\n\n    def testIndexOrderByTitle(self):\n        song_a = self.addSong(artist=self.addArtist(), title=\"A Title\")\n        song_b = self.addSong(artist=self.addArtist(), title=\"B Title\")\n        self.addToQueue(song_a)\n        self.addToQueue(song_b)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/queue?order_by=title\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_b.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/queue?order_by=title&order_direction=desc\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_b.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_a.id)\n\n    def testIndexOrderByArtist(self):\n        song_a = self.addSong(artist=self.addArtist(name=\"A Name\"))\n        song_b = self.addSong(artist=self.addArtist(name=\"B Name\"))\n        self.addToQueue(song_a)\n        self.addToQueue(song_b)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/queue?order_by=artist\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_b.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/queue?order_by=artist&order_direction=desc\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_b.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_a.id)\n\n    def testIndexOrderByAlbum(self):\n        album_a = self.addAlbum(title=\"A Title\")\n        album_b = self.addAlbum(title=\"B Title\")\n        song_a = self.addSong(artist=self.addArtist(), album=album_a)\n        song_b = self.addSong(artist=self.addArtist(), album=album_b)\n        self.addToQueue(song_a)\n        self.addToQueue(song_b)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/queue?order_by=album\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_b.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/queue?order_by=album&order_direction=desc\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_b.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_a.id)\n\n    def testIndexOrderByYear(self):\n        song_a = self.addSong(artist=self.addArtist(), year=2000)\n        song_b = self.addSong(artist=self.addArtist(), year=2001)\n        self.addToQueue(song_a)\n        self.addToQueue(song_b)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/queue?order_by=year\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_b.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/queue?order_by=year&order_direction=desc\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_b.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_a.id)\n\n    def testIndexOrderByGenre(self):\n        song_a = self.addSong(\n            artist=self.addArtist(),\n            genre=self.addGenre(name=\"A Genre\")\n        )\n        song_b = self.addSong(\n            artist=self.addArtist(),\n            genre=self.addGenre(name=\"B Genre\")\n        )\n        self.addToQueue(song_a)\n        self.addToQueue(song_b)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/queue?order_by=genre\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_b.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/queue?order_by=genre&order_direction=desc\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_b.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_a.id)\n\n    def testIndexOrderByCreated(self):\n        song_a = self.addSong(artist=self.addArtist())\n        song_b = self.addSong(artist=self.addArtist())\n        self.addToQueue(song_a)\n        self.addToQueue(song_b)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/queue?order_by=created\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_b.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/queue?order_by=created&order_direction=desc\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_b.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_a.id)\n\n    def testCount(self):\n        song_a = self.addSong(artist=self.addArtist())\n        song_b = self.addSong(artist=self.addArtist())\n        song_c = self.addSong(artist=self.addArtist())\n        self.addToQueue(song_a)\n        self.addToQueue(song_b)\n        self.addToQueue(song_c)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/queue?count=1\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertTrue(result[\"hasNextPage\"])\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/queue?count=3\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 3)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_b.id)\n        self.assertEquals(result[\"itemList\"][2][\"id\"], song_c.id)\n        self.assertFalse(result[\"hasNextPage\"])\n\n    def testCountAndPage(self):\n        song_a = self.addSong(artist=self.addArtist())\n        song_b = self.addSong(artist=self.addArtist())\n        song_c = self.addSong(artist=self.addArtist())\n        self.addToQueue(song_a)\n        self.addToQueue(song_b)\n        self.addToQueue(song_c)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/queue?count=1&page=1\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertTrue(result[\"hasNextPage\"])\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/queue?count=1&page=2\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_b.id)\n        self.assertTrue(result[\"hasNextPage\"])\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/queue?count=1&page=3\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_c.id)\n        self.assertFalse(result[\"hasNextPage\"])\n"
  },
  {
    "path": "jukebox/jukebox_core/tests/api_songs.py",
    "content": "# -*- coding: UTF-8 -*-\n\nimport random, simplejson\nfrom jukebox.jukebox_core import api\nfrom jukebox.jukebox_core.tests.api import ApiTestBase\n\n\nclass ApiSongsTest(ApiTestBase):\n    def testIndexEmpty(self):\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/songs\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 0)\n\n    def testIndex(self):\n        song = self.addSong(artist=self.addArtist())\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/songs\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song.id)\n\n    def testIndexWithSearchTermInTitle(self):\n        fixture = \"thisIsATestFixtureString\"\n        fixturePart = fixture[0:random.randint(5, len(fixture))]\n\n        song = self.addSong(artist=self.addArtist(), title=fixture)\n        self.addSong(artist=self.addArtist(), title=\"AAAAAAAAAAAAAA\")\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/songs?search_term=\" + fixturePart\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song.id)\n\n    def testIndexWithSearchTermInArtistName(self):\n        fixture = \"thisIsATestFixtureString\"\n        fixturePart = fixture[0:random.randint(5, len(fixture))]\n\n        song = self.addSong(artist=self.addArtist(name=fixture))\n        self.addSong(artist=self.addArtist(name=\"AAAAAAAAAAAAAA\"))\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/songs?search_term=\" + fixturePart\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song.id)\n\n    def testIndexWithSearchTermInAlbumTitle(self):\n        fixture = \"thisIsATestFixtureString\"\n        fixturePart = fixture[0:random.randint(5, len(fixture))]\n\n        artist = self.addArtist()\n        album = self.addAlbum(title=fixture)\n        song = self.addSong(artist=artist, album=album)\n        self.addSong(\n            artist=artist,\n            album=self.addAlbum(title=\"AAAAAAAAAAAAAA\")\n        )\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/songs?search_term=\" + fixturePart\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song.id)\n\n    def testIndexWithSearchTitle(self):\n        fixture = \"thisIsATestFixtureString\"\n        fixturePart = fixture[0:random.randint(5, len(fixture))]\n\n        song = self.addSong(artist=self.addArtist(), title=fixture)\n        self.addSong(artist=self.addArtist(), title=\"AAAAAAAAAAAAAA\")\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/songs?search_title=\" + fixturePart\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song.id)\n\n    def testIndexWithSearchArtistName(self):\n        fixture = \"thisIsATestFixtureString\"\n        fixturePart = fixture[0:random.randint(5, len(fixture))]\n\n        song = self.addSong(artist=self.addArtist(name=fixture))\n        self.addSong(artist=self.addArtist(name=\"AAAAAAAAAAAAAA\"))\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/songs?search_artist=\" + fixturePart\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song.id)\n\n    def testIndexWithSearchAlbumTitle(self):\n        fixture = \"thisIsATestFixtureString\"\n        fixturePart = fixture[0:random.randint(5, len(fixture))]\n\n        artist = self.addArtist()\n        album = self.addAlbum(title=fixture)\n        song = self.addSong(artist=artist, album=album)\n        self.addSong(\n            artist=artist,\n            album=self.addAlbum(title=\"AAAAAAAAAAAAAA\")\n        )\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/songs?search_album=\" + fixturePart\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song.id)\n\n    def testIndexWithFilterYear(self):\n        fixture = 2010\n        song = self.addSong(artist=self.addArtist(), year=fixture)\n        self.addSong(artist=self.addArtist(), year=2001)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/songs?filter_year=\" + str(fixture)\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song.id)\n\n    def testIndexWithFilterGenre(self):\n        genre = self.addGenre()\n        song = self.addSong(artist=self.addArtist(), genre=genre)\n        self.addSong(artist=self.addArtist(), genre=self.addGenre())\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/songs?filter_genre=\" + str(genre.id)\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song.id)\n\n    def testIndexWithFilterAlbumId(self):\n        artist = self.addArtist()\n        album = self.addAlbum(title=\"Foo\")\n        song = self.addSong(artist=artist, album=album)\n        self.addSong(artist=artist, album=self.addAlbum(title=\"Bar\"))\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/songs?filter_album_id=\" + str(album.id)\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song.id)\n\n    def testIndexWithFilterArtistId(self):\n        artist = self.addArtist()\n        song = self.addSong(artist=artist)\n        self.addSong(artist=self.addArtist())\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/songs?filter_artist_id=\" + str(artist.id)\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song.id)\n\n    def testIndexOrderByTitle(self):\n        song_a = self.addSong(artist=self.addArtist(), title=\"A Title\")\n        song_b = self.addSong(artist=self.addArtist(), title=\"B Title\")\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/songs?order_by=title\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_b.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/songs?order_by=title&order_direction=desc\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_b.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_a.id)\n\n    def testIndexOrderByArtist(self):\n        song_a = self.addSong(artist=self.addArtist(name=\"A Name\"))\n        song_b = self.addSong(artist=self.addArtist(name=\"B Name\"))\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/songs?order_by=artist\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_b.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/songs?order_by=artist&order_direction=desc\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_b.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_a.id)\n\n    def testIndexOrderByAlbum(self):\n        artist = self.addArtist()\n        album_a = self.addAlbum(title=\"A Title\")\n        album_b = self.addAlbum(title=\"B Title\")\n        song_a = self.addSong(artist=artist, album=album_a)\n        song_b = self.addSong(artist=artist, album=album_b)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/songs?order_by=album\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_b.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/songs?order_by=album&order_direction=desc\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_b.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_a.id)\n\n    def testIndexOrderByYear(self):\n        song_a = self.addSong(artist=self.addArtist(), year=2000)\n        song_b = self.addSong(artist=self.addArtist(), year=2001)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/songs?order_by=year\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_b.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/songs?order_by=year&order_direction=desc\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_b.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_a.id)\n\n    def testIndexOrderByGenre(self):\n        song_a = self.addSong(\n            artist=self.addArtist(),\n            genre=self.addGenre(name=\"A Name\")\n        )\n        song_b = self.addSong(\n            artist=self.addArtist(),\n            genre=self.addGenre(name=\"B Name\")\n        )\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/songs?order_by=genre\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_b.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/songs?order_by=genre&order_direction=desc\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_b.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_a.id)\n\n    def testIndexOrderByLength(self):\n        song_a = self.addSong(artist=self.addArtist(), length=100)\n        song_b = self.addSong(artist=self.addArtist(), length=200)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/songs?order_by=length\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_b.id)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/songs?order_by=length&order_direction=desc\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_b.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_a.id)\n\n    def testCount(self):\n        song_a = self.addSong(artist=self.addArtist())\n        song_b = self.addSong(artist=self.addArtist())\n        song_c = self.addSong(artist=self.addArtist())\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/songs?count=1\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertTrue(result[\"hasNextPage\"])\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/songs?count=3\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 3)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertEquals(result[\"itemList\"][1][\"id\"], song_b.id)\n        self.assertEquals(result[\"itemList\"][2][\"id\"], song_c.id)\n        self.assertFalse(result[\"hasNextPage\"])\n\n    def testCountAndPage(self):\n        song_a = self.addSong(artist=self.addArtist())\n        song_b = self.addSong(artist=self.addArtist())\n        song_c = self.addSong(artist=self.addArtist())\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/songs?count=1&page=1\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_a.id)\n        self.assertTrue(result[\"hasNextPage\"])\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/songs?count=1&page=2\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_b.id)\n        self.assertTrue(result[\"hasNextPage\"])\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/songs?count=1&page=3\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"id\"], song_c.id)\n        self.assertFalse(result[\"hasNextPage\"])\n\n    def testGetNextSongRandom(self):\n        song = self.addSong(artist=self.addArtist(), filename=__file__)\n\n        songs_api = api.songs()\n        result = songs_api.getNextSong()\n\n        self.assertEquals(result, song)\n\n        # check if song has been added to history\n        history_api = api.history()\n        result = history_api.index()\n\n        self.assertEqual(result[\"itemList\"][0][\"id\"], song.id)\n\n    def testGetNextSongFromQueue(self):\n        song = self.addSong(artist=self.addArtist(), filename=__file__)\n\n        # add to queue\n        queue_api = api.queue()\n        queue_api.set_user_id(self.user.id)\n        queue_api.add(song.id)\n\n        # get next song\n        songs_api = api.songs()\n        result = songs_api.getNextSong()\n        self.assertEquals(result, song)\n\n        # check if song has been added to history\n        history_api = api.history()\n        result = history_api.index()\n        self.assertEqual(result[\"itemList\"][0][\"id\"], song.id)\n\n        # check if song has been removed from queue\n        result = queue_api.index()\n        self.assertEqual(len(result[\"itemList\"]), 0)\n"
  },
  {
    "path": "jukebox/jukebox_core/tests/api_years.py",
    "content": "# -*- coding: UTF-8 -*-\n\nimport simplejson\nfrom jukebox.jukebox_core.tests.api import ApiTestBase\n\n\nclass ApiYearsTest(ApiTestBase):\n    def testIndexEmpty(self):\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/years\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 0)\n\n    def testIndex(self):\n        year = 2000\n        self.addSong(artist=self.addArtist(), year=year)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/years\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"year\"], year)\n\n    def testIndexOrderBy(self):\n        year_a = 2000\n        year_b = 2010\n        self.addSong(artist=self.addArtist(), year=year_a)\n        self.addSong(artist=self.addArtist(), year=year_b)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/years?order_by=year\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"year\"], year_a)\n        self.assertEquals(result[\"itemList\"][1][\"year\"], year_b)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/years?order_by=year&order_direction=desc\"\n            ).content\n        )\n\n        self.assertEquals(len(result[\"itemList\"]), 2)\n        self.assertEquals(result[\"itemList\"][0][\"year\"], year_b)\n        self.assertEquals(result[\"itemList\"][1][\"year\"], year_a)\n\n    def testCount(self):\n        year_a = 2000\n        year_b = 2005\n        year_c = 2010\n        self.addSong(artist=self.addArtist(), year=year_a)\n        self.addSong(artist=self.addArtist(), year=year_b)\n        self.addSong(artist=self.addArtist(), year=year_c)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/years?count=1\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"year\"], year_a)\n        self.assertTrue(result[\"hasNextPage\"])\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/years?count=3\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 3)\n        self.assertEquals(result[\"itemList\"][0][\"year\"], year_a)\n        self.assertEquals(result[\"itemList\"][1][\"year\"], year_b)\n        self.assertEquals(result[\"itemList\"][2][\"year\"], year_c)\n        self.assertFalse(result[\"hasNextPage\"])\n\n    def testCountAndPage(self):\n        year_a = 2000\n        year_b = 2005\n        year_c = 2010\n        self.addSong(artist=self.addArtist(), year=year_a)\n        self.addSong(artist=self.addArtist(), year=year_b)\n        self.addSong(artist=self.addArtist(), year=year_c)\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/years?count=1&page=1\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"year\"], year_a)\n        self.assertTrue(result[\"hasNextPage\"])\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/years?count=1&page=2\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"year\"], year_b)\n        self.assertTrue(result[\"hasNextPage\"])\n\n        result = simplejson.loads(\n            self.httpGet(\n                \"/api/v1/years?count=1&page=3\"\n            ).content\n        )\n        self.assertEquals(len(result[\"itemList\"]), 1)\n        self.assertEquals(result[\"itemList\"][0][\"year\"], year_c)\n        self.assertFalse(result[\"hasNextPage\"])\n"
  },
  {
    "path": "jukebox/jukebox_core/urls.py",
    "content": "# -*- coding: UTF-8 -*-\n\nfrom django.conf.urls import patterns, url\nimport views\n\nurlpatterns = patterns(\"\",\n    url(\n        r\"^api/v1/songs$\",\n        views.songs.as_view(),\n        name=\"jukebox_api_songs\"\n    ),\n    url(\n        r\"^api/v1/songs/skip$\",\n        views.songs_skip.as_view(),\n        name=\"jukebox_api_songs_skip\"\n    ),\n    url(\n        r\"^api/v1/songs/current\",\n        views.songs_current.as_view(),\n        name=\"jukebox_api_songs_current\"\n    ),\n    url(\n        r\"^api/v1/artists$\",\n        views.artists.as_view(),\n        name=\"jukebox_api_artists\"\n    ),\n    url(\n        r\"^api/v1/albums$\",\n        views.albums.as_view(),\n        name=\"jukebox_api_albums\"\n    ),\n    url(\n        r\"^api/v1/genres$\",\n        views.genres.as_view(),\n        name=\"jukebox_api_genres\"\n    ),\n    url(\n        r\"^api/v1/years$\",\n        views.years.as_view(),\n        name=\"jukebox_api_years\"\n    ),\n    url(\n        r\"^api/v1/history$\",\n        views.history.as_view(),\n        name=\"jukebox_api_history\"\n    ),\n    url(\n        r\"^api/v1/history/my$\",\n        views.history_my.as_view(),\n        name=\"jukebox_api_history_my\"\n    ),\n    url(\n        r\"^api/v1/favourites$\",\n        views.favourites.as_view(),\n        name=\"jukebox_api_favourites\"\n    ),\n    url(\n        r\"^api/v1/favourites/(?P<song_id>[0-9]+)$\",\n        views.favourites_item.as_view(),\n        name=\"jukebox_api_favourites_item\"\n    ),\n\n    url(\n        r\"^api/v1/queue$\",\n        views.queue.as_view(),\n        name=\"jukebox_api_queue\"\n    ),\n    url(\n        r\"^api/v1/queue/(?P<song_id>[0-9]+)$\",\n        views.queue_item.as_view(),\n        name=\"jukebox_api_queue_item\"\n    ),\n    url(\n        r\"^api/v1/ping$\",\n        views.ping.as_view(),\n        name=\"jukebox_api_ping\"\n    ),\n)\n"
  },
  {
    "path": "jukebox/jukebox_core/utils.py",
    "content": "# -*- coding: UTF-8 -*-\nfrom jukebox.jukebox_core.models import Artist, Album, Song, Genre\nfrom mutagen.easyid3 import EasyID3\nfrom mutagen.mp3 import MP3, HeaderNotFoundError\nfrom mutagen.id3 import ID3NoHeaderError\n\n\nclass FileIndexer:\n    def index(self, filename):\n        # skip already indexed\n        if self.is_indexed(filename):\n            return\n\n        try:\n            id3 = EasyID3(filename)\n            tags = {\n                \"artist\": None,\n                \"title\": None,\n                \"album\": None,\n                \"genre\": None,\n                \"date\": None,\n                \"length\": None,\n            }\n\n            for k, v in id3.items():\n                tags[k] = v[0].lower()\n\n            if tags[\"artist\"] is None or tags[\"title\"] is None:\n                print \"Artist or title not set in \" + \\\n                    filename + \" - skipping file\"\n                return\n\n            if tags[\"artist\"] is not None:\n                tags[\"artist\"], created = Artist.objects.get_or_create(\n                    Name=tags[\"artist\"]\n                )\n            if tags[\"album\"] is not None and tags[\"artist\"] is not None:\n                tags[\"album\"], created = Album.objects.get_or_create(\n                    Title=tags[\"album\"]\n                )\n            if tags[\"genre\"] is not None:\n                tags[\"genre\"], created = Genre.objects.get_or_create(\n                    Name=tags[\"genre\"]\n                )\n            if tags[\"date\"] is not None:\n                try:\n                    tags[\"date\"] = int(tags[\"date\"])\n                except ValueError:\n                    tags[\"date\"] = None\n\n            audio = MP3(filename)\n            tags[\"length\"] = int(audio.info.length)\n\n            song = Song(\n                Artist=tags[\"artist\"],\n                Album=tags[\"album\"],\n                Genre=tags[\"genre\"],\n                Title=tags[\"title\"],\n                Year=tags[\"date\"],\n                Length=tags[\"length\"],\n                Filename=filename\n            )\n            song.save()\n        except HeaderNotFoundError:\n            print \"File contains invalid header data: \" + filename\n        except ID3NoHeaderError:\n            print \"File does not contain an id3 header: \" + filename\n\n    def delete(self, filename):\n        # single file\n        Song.objects.filter(Filename__exact=filename).delete()\n        # directory\n        Song.objects.filter(Filename__startswith=filename).delete()\n\n    def is_indexed(self, filename):\n        data = Song.objects.filter(Filename__exact=filename)\n        if not data:\n            return False\n        return True\n"
  },
  {
    "path": "jukebox/jukebox_core/views.py",
    "content": "# -*- coding: UTF-8 -*-\n\nfrom django.core.urlresolvers import reverse\nfrom django.core.exceptions import ObjectDoesNotExist\nfrom rest_framework.views import APIView\nfrom rest_framework.response import Response\nfrom rest_framework import status\nfrom rest_framework.permissions import IsAuthenticated\nimport api, base64\nimport forms\n\n\nclass JukeboxAPIView(APIView):\n    def api_set_user_id(self, request, api):\n        if request.user.is_authenticated():\n            api.set_user_id(request.user.id)\n        return api\n\n\nclass songs(JukeboxAPIView):\n    permissions = (IsAuthenticated, )\n\n    def get(self, request):\n        request.session.modified = True\n\n        page = 1\n        songs_api = api.songs()\n        songs_api = self.api_set_user_id(request, songs_api)\n\n        form = forms.SongsForm(request.GET)\n        if form.is_valid():\n            if not form.cleaned_data[\"search_term\"] == \"\":\n                songs_api.set_search_term(\n                    form.cleaned_data[\"search_term\"]\n                )\n            if not form.cleaned_data[\"search_title\"] == \"\":\n                songs_api.set_search_title(\n                    form.cleaned_data[\"search_title\"]\n                )\n            if not form.cleaned_data[\"search_artist\"] == \"\":\n                songs_api.set_search_artist_name(\n                    form.cleaned_data[\"search_artist\"]\n                )\n            if not form.cleaned_data[\"search_album\"] == \"\":\n                songs_api.set_search_album_title(\n                    form.cleaned_data[\"search_album\"]\n                )\n\n            if not form.cleaned_data[\"filter_artist_id\"] is None:\n                songs_api.set_filter_artist_id(\n                    form.cleaned_data[\"filter_artist_id\"]\n                )\n            if not form.cleaned_data[\"filter_album_id\"] is None:\n                songs_api.set_filter_album_id(\n                    form.cleaned_data[\"filter_album_id\"]\n                )\n            if not form.cleaned_data[\"filter_genre\"] is None:\n                songs_api.set_filter_genre(\n                    form.cleaned_data[\"filter_genre\"]\n                )\n            if not form.cleaned_data[\"filter_year\"] is None:\n                songs_api.set_filter_year(\n                    form.cleaned_data[\"filter_year\"]\n                )\n\n            if (not form.cleaned_data[\"order_by\"] == \"\" and\n                not form.cleaned_data[\"order_direction\"] == \"\"):\n                songs_api.set_order_by(\n                    form.cleaned_data[\"order_by\"],\n                    form.cleaned_data[\"order_direction\"]\n                )\n            elif not form.cleaned_data[\"order_by\"] == \"\":\n                songs_api.set_order_by(form.cleaned_data[\"order_by\"])\n\n            if not form.cleaned_data[\"count\"] is None:\n                songs_api.set_count(form.cleaned_data[\"count\"])\n            if not form.cleaned_data[\"page\"] is None:\n                page = form.cleaned_data[\"page\"]\n\n        result = songs_api.index(page)\n        result[\"form\"] = form.cleaned_data\n        return Response(\n            data=result\n        )\n\n\nclass songs_current(JukeboxAPIView):\n    permissions = (IsAuthenticated, )\n\n    def get(self, request):\n        request.session.modified = True\n\n        history = api.history()\n        current = {}\n        try:\n            current = history.getCurrent()\n        except:\n            pass\n\n        return Response(\n            data=current\n        )\n\n\nclass songs_skip(JukeboxAPIView):\n    permissions = (IsAuthenticated, )\n\n    def get(self, request):\n        request.session.modified = True\n\n        songs_api = api.songs()\n        songs_api.skipCurrentSong()\n        return Response(\"\")\n\nclass artists(JukeboxAPIView):\n    permissions = (IsAuthenticated, )\n\n    def get(self, request):\n        request.session.modified = True\n\n        page = 1\n        artists_api = api.artists()\n\n        form = forms.ArtistsForm(request.GET)\n        if form.is_valid():\n            if (not form.cleaned_data[\"order_by\"] == \"\" and\n                not form.cleaned_data[\"order_direction\"] == \"\"):\n                artists_api.set_order_by(\n                    form.cleaned_data[\"order_by\"],\n                    form.cleaned_data[\"order_direction\"]\n                )\n            elif not form.cleaned_data[\"order_by\"] == \"\":\n                artists_api.set_order_by(form.cleaned_data[\"order_by\"])\n\n            if not form.cleaned_data[\"count\"] is None:\n                artists_api.set_count(form.cleaned_data[\"count\"])\n            if not form.cleaned_data[\"page\"] is None:\n                page = form.cleaned_data[\"page\"]\n\n        return Response(\n            data=artists_api.index(page)\n        )\n\n\nclass albums(JukeboxAPIView):\n    permissions = (IsAuthenticated, )\n\n    def get(self, request):\n        request.session.modified = True\n\n        page = 1\n        albums_api = api.albums()\n\n        form = forms.AlbumsForm(request.GET)\n        if form.is_valid():\n            if (not form.cleaned_data[\"order_by\"] == \"\" and\n                not form.cleaned_data[\"order_direction\"] == \"\"):\n                albums_api.set_order_by(\n                    form.cleaned_data[\"order_by\"],\n                    form.cleaned_data[\"order_direction\"]\n                )\n            elif not form.cleaned_data[\"order_by\"] == \"\":\n                albums_api.set_order_by(form.cleaned_data[\"order_by\"])\n\n            if not form.cleaned_data[\"count\"] is None:\n                albums_api.set_count(form.cleaned_data[\"count\"])\n            if not form.cleaned_data[\"page\"] is None:\n                page = form.cleaned_data[\"page\"]\n\n        return Response(\n            data=albums_api.index(page)\n        )\n\n\nclass genres(JukeboxAPIView):\n    permissions = (IsAuthenticated, )\n\n    def get(self, request):\n        request.session.modified = True\n\n        page = 1\n        genres_api = api.genres()\n\n        form = forms.GenresForm(request.GET)\n        if form.is_valid():\n            if (not form.cleaned_data[\"order_by\"] == \"\" and\n                not form.cleaned_data[\"order_direction\"] == \"\"):\n                genres_api.set_order_by(\n                    form.cleaned_data[\"order_by\"],\n                    form.cleaned_data[\"order_direction\"]\n                )\n            elif not form.cleaned_data[\"order_by\"] == \"\":\n                genres_api.set_order_by(form.cleaned_data[\"order_by\"])\n\n            if not form.cleaned_data[\"count\"] is None:\n                genres_api.set_count(form.cleaned_data[\"count\"])\n            if not form.cleaned_data[\"page\"] is None:\n                page = form.cleaned_data[\"page\"]\n\n        return Response(\n            data=genres_api.index(page)\n        )\n\n\nclass years(JukeboxAPIView):\n    permissions = (IsAuthenticated, )\n\n    def get(self, request):\n        request.session.modified = True\n\n        page = 1\n        years_api = api.years()\n\n        form = forms.YearsForm(request.GET)\n        if form.is_valid():\n            if (not form.cleaned_data[\"order_by\"] == \"\" and\n                not form.cleaned_data[\"order_direction\"] == \"\"):\n                years_api.set_order_by(\n                    form.cleaned_data[\"order_by\"],\n                    form.cleaned_data[\"order_direction\"]\n                )\n            elif not form.cleaned_data[\"order_by\"] == \"\":\n                years_api.set_order_by(form.cleaned_data[\"order_by\"])\n\n            if not form.cleaned_data[\"count\"] is None:\n                years_api.set_count(form.cleaned_data[\"count\"])\n            if not form.cleaned_data[\"page\"] is None:\n                page = form.cleaned_data[\"page\"]\n\n        return Response(\n            data=years_api.index(page)\n        )\n\n\nclass history(JukeboxAPIView):\n    permissions = (IsAuthenticated, )\n\n    def get(self, request):\n        request.session.modified = True\n\n        page = 1\n        history_api = api.history()\n        history_api = self.api_set_user_id(request, history_api)\n\n        form = forms.HistoryForm(request.GET)\n        if form.is_valid():\n            if (not form.cleaned_data[\"order_by\"] == \"\" and\n                not form.cleaned_data[\"order_direction\"] == \"\"):\n                history_api.set_order_by(\n                    form.cleaned_data[\"order_by\"],\n                    form.cleaned_data[\"order_direction\"]\n                )\n            elif not form.cleaned_data[\"order_by\"] == \"\":\n                history_api.set_order_by(form.cleaned_data[\"order_by\"])\n\n            if not form.cleaned_data[\"count\"] is None:\n                history_api.set_count(form.cleaned_data[\"count\"])\n            if not form.cleaned_data[\"page\"] is None:\n                page = form.cleaned_data[\"page\"]\n\n        return Response(\n            data=history_api.index(page)\n        )\n\n\nclass history_my(JukeboxAPIView):\n    permissions = (IsAuthenticated, )\n\n    def get(self, request):\n        request.session.modified = True\n\n        page = 1\n        history_api = api.history_my()\n        history_api = self.api_set_user_id(request, history_api)\n\n        form = forms.HistoryForm(request.GET)\n        if form.is_valid():\n            if (not form.cleaned_data[\"order_by\"] == \"\" and\n                not form.cleaned_data[\"order_direction\"] == \"\"):\n                history_api.set_order_by(\n                    form.cleaned_data[\"order_by\"],\n                    form.cleaned_data[\"order_direction\"]\n                )\n            elif not form.cleaned_data[\"order_by\"] == \"\":\n                history_api.set_order_by(form.cleaned_data[\"order_by\"])\n\n            if not form.cleaned_data[\"count\"] is None:\n                history_api.set_count(form.cleaned_data[\"count\"])\n            if not form.cleaned_data[\"page\"] is None:\n                page = form.cleaned_data[\"page\"]\n\n        return Response(\n            data=history_api.index(page)\n        )\n\n\nclass queue(JukeboxAPIView):\n    permissions = (IsAuthenticated, )\n    form = forms.IdForm\n\n    def get(self, request):\n        request.session.modified = True\n\n        page = 1\n        queue_api = api.queue()\n        queue_api = self.api_set_user_id(request, queue_api)\n\n        form = forms.QueueForm(request.GET)\n        if form.is_valid():\n            if (not form.cleaned_data[\"order_by\"] == \"\" and\n                not form.cleaned_data[\"order_direction\"] == \"\"):\n                queue_api.set_order_by(\n                    form.cleaned_data[\"order_by\"],\n                    form.cleaned_data[\"order_direction\"]\n                )\n            elif not form.cleaned_data[\"order_by\"] == \"\":\n                queue_api.set_order_by(form.cleaned_data[\"order_by\"])\n\n            if not form.cleaned_data[\"count\"] is None:\n                queue_api.set_count(form.cleaned_data[\"count\"])\n            if not form.cleaned_data[\"page\"] is None:\n                page = form.cleaned_data[\"page\"]\n\n        result = queue_api.index(page)\n        for k, v in enumerate(result[\"itemList\"]):\n            result[\"itemList\"][k][\"url\"] = reverse(\n                \"jukebox_api_queue_item\",\n                kwargs={\"song_id\": v[\"id\"]}\n            )\n        return Response(\n            data=result\n        )\n\n    def post(self, request):\n        request.session.modified = True\n\n        queue_api = api.queue()\n        queue_api = self.api_set_user_id(request, queue_api)\n\n        try:\n            song_id = queue_api.add(self.request.POST[\"id\"])\n            return Response(\n                status=status.HTTP_201_CREATED,\n                data={\n                    'id': int(self.request.POST['id'])\n                },\n                headers={\"Location\": reverse(\n                    \"jukebox_api_queue_item\",\n                    kwargs={\"song_id\": song_id}\n                )}\n            )\n        except ObjectDoesNotExist:\n            return Response(status=status.HTTP_404_NOT_FOUND)\n        except Exception, e:\n            print e\n            return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR)\n\n\nclass queue_item(JukeboxAPIView):\n    permissions = (IsAuthenticated, )\n    form = forms.IdForm\n\n    def get(self, request, song_id):\n        request.session.modified = True\n\n        queue_api = api.queue()\n        queue_api = self.api_set_user_id(request, queue_api)\n\n        try:\n            item = queue_api.get(song_id)\n            item[\"url\"] = reverse(\n                \"jukebox_api_queue_item\",\n                kwargs={\"song_id\": item[\"id\"]}\n            )\n            return Response(\n                data=item\n            )\n        except ObjectDoesNotExist:\n            return Response(status=status.HTTP_404_NOT_FOUND)\n        except Exception, e:\n            print e\n            return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR)\n\n    def delete(self, request, song_id):\n        request.session.modified = True\n\n        queue_api = api.queue()\n        queue_api = self.api_set_user_id(request, queue_api)\n\n        try:\n            return Response(\n                data=queue_api.remove(song_id)\n            )\n        except ObjectDoesNotExist:\n            return Response(status=status.HTTP_404_NOT_FOUND)\n        except:\n            return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR)\n\n\nclass favourites(JukeboxAPIView):\n    permissions = (IsAuthenticated, )\n    form = forms.IdForm\n\n    def get(self, request):\n        request.session.modified = True\n\n        page = 1\n        favourites_api = api.favourites()\n        favourites_api = self.api_set_user_id(request, favourites_api)\n\n        form = forms.FavouritesForm(request.GET)\n        if form.is_valid():\n            if (not form.cleaned_data[\"order_by\"] == \"\" and\n                not form.cleaned_data[\"order_direction\"] == \"\"):\n                favourites_api.set_order_by(\n                    form.cleaned_data[\"order_by\"],\n                    form.cleaned_data[\"order_direction\"]\n                )\n            elif not form.cleaned_data[\"order_by\"] == \"\":\n                favourites_api.set_order_by(form.cleaned_data[\"order_by\"])\n\n            if not form.cleaned_data[\"count\"] is None:\n                favourites_api.set_count(form.cleaned_data[\"count\"])\n            if not form.cleaned_data[\"page\"] is None:\n                page = form.cleaned_data[\"page\"]\n\n        result = favourites_api.index(page)\n        for k, v in enumerate(result[\"itemList\"]):\n            result[\"itemList\"][k][\"url\"] = reverse(\n                \"jukebox_api_favourites_item\",\n                kwargs={\"song_id\": v[\"id\"]}\n            )\n        return Response(\n            data=result\n        )\n\n    def post(self, request):\n        request.session.modified = True\n\n        favourites_api = api.favourites()\n        favourites_api = self.api_set_user_id(request, favourites_api)\n\n        try:\n            song_id = favourites_api.add(self.request.POST[\"id\"])\n            return Response(\n                status=status.HTTP_201_CREATED,\n                data={\n                    'id': int(self.request.POST['id']),\n                },\n                headers={\"Location\": reverse(\n                    \"jukebox_api_favourites_item\",\n                    kwargs={\"song_id\": song_id}\n                )}\n            )\n        except ObjectDoesNotExist:\n            return Response(status=status.HTTP_404_NOT_FOUND)\n        except Exception, e:\n            print e\n            return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR)\n\n\nclass favourites_item(JukeboxAPIView):\n    permissions = (IsAuthenticated, )\n    form = forms.IdForm\n\n    def get(self, request, song_id):\n        request.session.modified = True\n\n        favourites_api = api.favourites()\n        favourites_api = self.api_set_user_id(request, favourites_api)\n\n        try:\n            item = favourites_api.get(song_id)\n            item[\"url\"] = reverse(\n                \"jukebox_api_favourites_item\",\n                kwargs={\"song_id\": item[\"id\"]}\n            )\n            return Response(\n                data=item\n            )\n        except ObjectDoesNotExist:\n            return Response(status=status.HTTP_404_NOT_FOUND)\n        except Exception, e:\n            print e\n            return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR)\n\n    def delete(self, request, song_id):\n        request.session.modified = True\n\n        favourites_api = api.favourites()\n        favourites_api = self.api_set_user_id(request, favourites_api)\n\n        try:\n            return Response(\n                data=favourites_api.remove(song_id)\n            )\n        except ObjectDoesNotExist:\n            return Response(status=status.HTTP_404_NOT_FOUND)\n        except Exception, e:\n            print e\n            return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR)\n\n\nclass ping(JukeboxAPIView):\n    def get(self, request):\n        request.session.modified = True\n        return Response(\n            data= {\n                \"ping\": True\n            }\n        )\n"
  },
  {
    "path": "jukebox/jukebox_web/__init__.py",
    "content": ""
  },
  {
    "path": "jukebox/jukebox_web/locale/de/LC_MESSAGES/django.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same license as the PACKAGE package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: PACKAGE VERSION\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2014-11-04 20:03+0100\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: Jens Nistler <opensource@jensnistler.de>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"Language: pt_BR\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=2; plural=(n != 1)\\n\"\n\n#: templates/index.html:5 templates/index.html.py:6 templates/login.html:5\n#: templates/login.html.py:6\nmsgid \"Democratic Jukebox - your democratic music player\"\nmsgstr \"Democratic Jukebox - Dein demokratischer Musikspieler\"\n\n#: templates/index.html:15 templates/login.html:15\nmsgid \"Jukebox logo\"\nmsgstr \"Jukebox Logo\"\n\n#: templates/index.html:18\nmsgid \"Account options\"\nmsgstr \"Benutzereinstellungen\"\n\n#: templates/index.html:25\nmsgid \"Search term\"\nmsgstr \"Suchbegriff\"\n\n#: templates/index.html:28 templates/index.html.py:140\nmsgid \"Search\"\nmsgstr \"Suchen\"\n\n#: templates/index.html:35\nmsgid \"Now playing\"\nmsgstr \"Aktuell läuft:\"\n\n#: templates/index.html:45\nmsgid \"Navigation\"\nmsgstr \"Navigation\"\n\n#: templates/index.html:50\nmsgid \"Queue\"\nmsgstr \"Warteschlange\"\n\n#: templates/index.html:55\nmsgid \"History\"\nmsgstr \"Zuletzt gespielt\"\n\n#: templates/index.html:60\nmsgid \"My History\"\nmsgstr \"Von mir gewählt\"\n\n#: templates/index.html:65\nmsgid \"Favourites\"\nmsgstr \"Favoriten\"\n\n#: templates/index.html:71\nmsgid \"Music\"\nmsgstr \"Musik\"\n\n#: templates/index.html:76\nmsgid \"Songs\"\nmsgstr \"Titel\"\n\n#: templates/index.html:81\nmsgid \"Artists\"\nmsgstr \"Künstler\"\n\n#: templates/index.html:86\nmsgid \"Albums\"\nmsgstr \"Alben\"\n\n#: templates/index.html:91\nmsgid \"Genres\"\nmsgstr \"Musikrichtungen\"\n\n#: templates/index.html:96\nmsgid \"Years\"\nmsgstr \"Jahre\"\n\n#: templates/index.html:109\nmsgid \"Title\"\nmsgstr \"Titel\"\n\n#: templates/index.html:113\nmsgid \"Artist\"\nmsgstr \"Künstler\"\n\n#: templates/index.html:117\nmsgid \"Album\"\nmsgstr \"Album\"\n\n#: templates/index.html:121\nmsgid \"Genre\"\nmsgstr \"Musikrichtung\"\n\n#: templates/index.html:123\nmsgid \"All genres\"\nmsgstr \"Alle Musikrichtungen\"\n\n#: templates/index.html:130\nmsgid \"Year\"\nmsgstr \"Jahr\"\n\n#: templates/index.html:132\nmsgid \"All years\"\nmsgstr \"Alle Jahre\"\n\n#: templates/index.html:139\nmsgid \"Reset\"\nmsgstr \"Leeren\"\n\n#: templates/index.html:146\nmsgid \"Switch language\"\nmsgstr \"Sprache ändern\"\n\n#: templates/index.html:149\nmsgid \"English\"\nmsgstr \"Englisch\"\n\n#: templates/index.html:152\nmsgid \"German\"\nmsgstr \"Deutsch\"\n\n#: templates/index.html:155\nmsgid \"French\"\nmsgstr \"Französisch\"\n\n#: templates/index.html:158\nmsgid \"Brazilian Portuguese\"\nmsgstr \"Brasilianisches Portugiesisch\"\n\n#: templates/index.html:161\nmsgid \"Misc\"\nmsgstr \"Verschiedenes\"\n\n#: templates/index.html:164\nmsgid \"Logout\"\nmsgstr \"Abmelden\"\n\n#: templates/login.html:21\nmsgid \"Login\"\nmsgstr \"Login\"\n\n#: templates/login.html:35\n#, python-format\nmsgid \"Login with your %(backend)s account\"\nmsgstr \"Melde Dich mit Deinem %(backend)s Konto an\"\n"
  },
  {
    "path": "jukebox/jukebox_web/locale/de/LC_MESSAGES/djangojs.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same license as the PACKAGE package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: PACKAGE VERSION\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2011-11-19 14:24+0100\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: Jens Nistler <opensource@jensnistler.de>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"Language: \\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=2; plural=(n != 1)\\n\"\n\n#: static/js/music.js:1\nmsgid \"function\"\nmsgstr \"\"\n\n#: static/js/music.js:105 static/js/music.js.py:106 static/js/music.js:414\n#: static/js/music.js.py:436 static/js/music.js:458 static/js/music.js.py:475\nmsgid \"Revoke vote\"\nmsgstr \"Stimme zurückziehen\"\n\n#: static/js/music.js:122 static/js/music.js.py:123 static/js/music.js:439\n#: static/js/music.js.py:461 static/js/music.js:478\nmsgid \"Vote to play\"\nmsgstr \"Zum Abspielen wählen\"\n\n#: static/js/music.js:140 static/js/music.js.py:141 static/js/music.js:417\nmsgid \"Support vote\"\nmsgstr \"Wahl unterstützen\"\n\n#: static/js/music.js:170 static/js/music.js.py:171 static/js/music.js:420\n#: static/js/music.js.py:442 static/js/music.js:463 static/js/music.js.py:481\nmsgid \"Remove from favourites\"\nmsgstr \"Aus Favoriten entfernen\"\n\n#: static/js/music.js:186 static/js/music.js.py:187 static/js/music.js:423\n#: static/js/music.js.py:445 static/js/music.js:484\nmsgid \"Add to favourites\"\nmsgstr \"Zu Favoriten hinzufügen\"\n\n#: static/js/music.js:286\nmsgid \"No data found\"\nmsgstr \"Keine Daten gefunden\"\n\n#: static/js/music.js:351 static/js/music.js.py:359 static/js/music.js:367\n#: static/js/music.js.py:375 static/js/music.js:388\nmsgid \"Title\"\nmsgstr \"Titel\"\n\n#: static/js/music.js:352 static/js/music.js.py:360 static/js/music.js:368\n#: static/js/music.js.py:376 static/js/music.js:389\nmsgid \"Artist\"\nmsgstr \"Künstler\"\n\n#: static/js/music.js:353 static/js/music.js.py:361 static/js/music.js:369\n#: static/js/music.js.py:377\nmsgid \"Album\"\nmsgstr \"Album\"\n\n#: static/js/music.js:354 static/js/music.js.py:362\nmsgid \"Votes\"\nmsgstr \"Stimmen\"\n\n#: static/js/music.js:355\nmsgid \"First voted\"\nmsgstr \"Erste Stimme erhalten\"\n\n#: static/js/music.js:363 static/js/music.js.py:371\nmsgid \"Date added\"\nmsgstr \"Hinzugefügt\"\n\n#: static/js/music.js:370 static/js/music.js.py:378\nmsgid \"Genre\"\nmsgstr \"Musikrichtung\"\n\n#: static/js/music.js:379 static/js/music.js.py:397\nmsgid \"Year\"\nmsgstr \"Jahr\"\n\n#: static/js/music.js:380\nmsgid \"Length\"\nmsgstr \"Dauer\"\n\n#: static/js/music.js:384 static/js/music.js.py:393\nmsgid \"Name\"\nmsgstr \"Name\"\n\n#: static/js/music.js:451\nmsgid \"Autoplay\"\nmsgstr \"automatisch\"\n"
  },
  {
    "path": "jukebox/jukebox_web/locale/en/LC_MESSAGES/django.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same license as the PACKAGE package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: PACKAGE VERSION\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2014-11-04 20:03+0100\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: Jens Nistler <opensource@jensnistler.de>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"Language: \\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\n#: templates/index.html:5 templates/index.html.py:6 templates/login.html:5\n#: templates/login.html.py:6\nmsgid \"Democratic Jukebox - your democratic music player\"\nmsgstr \"Democratic Jukebox - your democratic music player\"\n\n#: templates/index.html:15 templates/login.html:15\nmsgid \"Jukebox logo\"\nmsgstr \"Jukebox logo\"\n\n#: templates/index.html:18\nmsgid \"Account options\"\nmsgstr \"Account options\"\n\n#: templates/index.html:25\nmsgid \"Search term\"\nmsgstr \"Search term\"\n\n#: templates/index.html:28 templates/index.html.py:140\nmsgid \"Search\"\nmsgstr \"Search\"\n\n#: templates/index.html:35\nmsgid \"Now playing\"\nmsgstr \"Now playing:\"\n\n#: templates/index.html:45\nmsgid \"Navigation\"\nmsgstr \"Navigation\"\n\n#: templates/index.html:50\nmsgid \"Queue\"\nmsgstr \"Queue\"\n\n#: templates/index.html:55\nmsgid \"History\"\nmsgstr \"History\"\n\n#: templates/index.html:60\n#, fuzzy\nmsgid \"My History\"\nmsgstr \"My History\"\n\n#: templates/index.html:65\nmsgid \"Favourites\"\nmsgstr \"Favourites\"\n\n#: templates/index.html:71\nmsgid \"Music\"\nmsgstr \"Music\"\n\n#: templates/index.html:76\nmsgid \"Songs\"\nmsgstr \"Songs\"\n\n#: templates/index.html:81\nmsgid \"Artists\"\nmsgstr \"Artists\"\n\n#: templates/index.html:86\nmsgid \"Albums\"\nmsgstr \"Albums\"\n\n#: templates/index.html:91\nmsgid \"Genres\"\nmsgstr \"Genres\"\n\n#: templates/index.html:96\nmsgid \"Years\"\nmsgstr \"Years\"\n\n#: templates/index.html:109\nmsgid \"Title\"\nmsgstr \"Title\"\n\n#: templates/index.html:113\nmsgid \"Artist\"\nmsgstr \"Artist\"\n\n#: templates/index.html:117\nmsgid \"Album\"\nmsgstr \"Album\"\n\n#: templates/index.html:121\nmsgid \"Genre\"\nmsgstr \"Genre\"\n\n#: templates/index.html:123\nmsgid \"All genres\"\nmsgstr \"All genres\"\n\n#: templates/index.html:130\nmsgid \"Year\"\nmsgstr \"Year\"\n\n#: templates/index.html:132\nmsgid \"All years\"\nmsgstr \"All years\"\n\n#: templates/index.html:139\nmsgid \"Reset\"\nmsgstr \"Reset\"\n\n#: templates/index.html:146\nmsgid \"Switch language\"\nmsgstr \"Switch language\"\n\n#: templates/index.html:149\nmsgid \"English\"\nmsgstr \"English\"\n\n#: templates/index.html:152\nmsgid \"German\"\nmsgstr \"German\"\n\n#: templates/index.html:155\nmsgid \"French\"\nmsgstr \"\"\n\n#: templates/index.html:158\nmsgid \"Brazilian Portuguese\"\nmsgstr \"Brazilian Portuguese\"\n\n#: templates/index.html:161\nmsgid \"Misc\"\nmsgstr \"Misc\"\n\n#: templates/index.html:164\nmsgid \"Logout\"\nmsgstr \"Logout\"\n\n#: templates/login.html:21\nmsgid \"Login\"\nmsgstr \"Login\"\n\n#: templates/login.html:35\n#, python-format\nmsgid \"Login with your %(backend)s account\"\nmsgstr \"Login with your %(backend)s account\"\n"
  },
  {
    "path": "jukebox/jukebox_web/locale/en/LC_MESSAGES/djangojs.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same license as the PACKAGE package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: PACKAGE VERSION\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2011-11-19 14:24+0100\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: Jens Nistler <opensource@jensnistler.de>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"Language: \\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\n#: static/js/music.js:1\nmsgid \"function\"\nmsgstr \"\"\n\n#: static/js/music.js:105 static/js/music.js.py:106 static/js/music.js:414\n#: static/js/music.js.py:436 static/js/music.js:458 static/js/music.js.py:475\nmsgid \"Revoke vote\"\nmsgstr \"Revoke vote\"\n\n#: static/js/music.js:122 static/js/music.js.py:123 static/js/music.js:439\n#: static/js/music.js.py:461 static/js/music.js:478\nmsgid \"Vote to play\"\nmsgstr \"Vote to play\"\n\n#: static/js/music.js:140 static/js/music.js.py:141 static/js/music.js:417\nmsgid \"Support vote\"\nmsgstr \"Support vote\"\n\n#: static/js/music.js:170 static/js/music.js.py:171 static/js/music.js:420\n#: static/js/music.js.py:442 static/js/music.js:463 static/js/music.js.py:481\nmsgid \"Remove from favourites\"\nmsgstr \"Remove from favourites\"\n\n#: static/js/music.js:186 static/js/music.js.py:187 static/js/music.js:423\n#: static/js/music.js.py:445 static/js/music.js:484\nmsgid \"Add to favourites\"\nmsgstr \"Add to favourites\"\n\n#: static/js/music.js:286\nmsgid \"No data found\"\nmsgstr \"No data found\"\n\n#: static/js/music.js:351 static/js/music.js.py:359 static/js/music.js:367\n#: static/js/music.js.py:375 static/js/music.js:388\nmsgid \"Title\"\nmsgstr \"Title\"\n\n#: static/js/music.js:352 static/js/music.js.py:360 static/js/music.js:368\n#: static/js/music.js.py:376 static/js/music.js:389\nmsgid \"Artist\"\nmsgstr \"Artist\"\n\n#: static/js/music.js:353 static/js/music.js.py:361 static/js/music.js:369\n#: static/js/music.js.py:377\nmsgid \"Album\"\nmsgstr \"Album\"\n\n#: static/js/music.js:354 static/js/music.js.py:362\nmsgid \"Votes\"\nmsgstr \"Votes\"\n\n#: static/js/music.js:355\nmsgid \"First voted\"\nmsgstr \"First voted\"\n\n#: static/js/music.js:363 static/js/music.js.py:371\nmsgid \"Date added\"\nmsgstr \"Date added\"\n\n#: static/js/music.js:370 static/js/music.js.py:378\nmsgid \"Genre\"\nmsgstr \"Genre\"\n\n#: static/js/music.js:379 static/js/music.js.py:397\nmsgid \"Year\"\nmsgstr \"Year\"\n\n#: static/js/music.js:380\nmsgid \"Length\"\nmsgstr \"Length\"\n\n#: static/js/music.js:384 static/js/music.js.py:393\nmsgid \"Name\"\nmsgstr \"Name\"\n\n#: static/js/music.js:451\nmsgid \"Autoplay\"\nmsgstr \"Autoplay\"\n"
  },
  {
    "path": "jukebox/jukebox_web/locale/fr/LC_MESSAGES/django.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same license as the PACKAGE package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: PACKAGE VERSION\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2014-11-04 20:03+0100\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"Language: \\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=2; plural=(n > 1)\\n\"\n\n#: templates/index.html:5 templates/index.html.py:6 templates/login.html:5\n#: templates/login.html.py:6\nmsgid \"Democratic Jukebox - your democratic music player\"\nmsgstr \"\"\n\n#: templates/index.html:15 templates/login.html:15\nmsgid \"Jukebox logo\"\nmsgstr \"\"\n\n#: templates/index.html:18\nmsgid \"Account options\"\nmsgstr \"Options du compte\"\n\n#: templates/index.html:25\nmsgid \"Search term\"\nmsgstr \"Mots recherchés\"\n\n#: templates/index.html:28 templates/index.html.py:140\nmsgid \"Search\"\nmsgstr \"Recherche\"\n\n#: templates/index.html:35\nmsgid \"Now playing\"\nmsgstr \"Lecture en cours\"\n\n#: templates/index.html:45\nmsgid \"Navigation\"\nmsgstr \"\"\n\n#: templates/index.html:50\nmsgid \"Queue\"\nmsgstr \"File\"\n\n#: templates/index.html:55\nmsgid \"History\"\nmsgstr \"Historique\"\n\n#: templates/index.html:60\nmsgid \"My History\"\nmsgstr \"Mon historique\"\n\n#: templates/index.html:65\nmsgid \"Favourites\"\nmsgstr \"Favoris\"\n\n#: templates/index.html:71\nmsgid \"Music\"\nmsgstr \"Musique\"\n\n#: templates/index.html:76\nmsgid \"Songs\"\nmsgstr \"Chansons\"\n\n#: templates/index.html:81\nmsgid \"Artists\"\nmsgstr \"Artistes\"\n\n#: templates/index.html:86\nmsgid \"Albums\"\nmsgstr \"\"\n\n#: templates/index.html:91\nmsgid \"Genres\"\nmsgstr \"\"\n\n#: templates/index.html:96\nmsgid \"Years\"\nmsgstr \"Ans\"\n\n#: templates/index.html:109\nmsgid \"Title\"\nmsgstr \"Titre\"\n\n#: templates/index.html:113\nmsgid \"Artist\"\nmsgstr \"Artiste\"\n\n#: templates/index.html:117\nmsgid \"Album\"\nmsgstr \"\"\n\n#: templates/index.html:121\nmsgid \"Genre\"\nmsgstr \"\"\n\n#: templates/index.html:123\nmsgid \"All genres\"\nmsgstr \"Tous les genres\"\n\n#: templates/index.html:130\nmsgid \"Year\"\nmsgstr \"Année\"\n\n#: templates/index.html:132\nmsgid \"All years\"\nmsgstr \"Tous les ans\"\n\n#: templates/index.html:139\nmsgid \"Reset\"\nmsgstr \"Réinitialiser\"\n\n#: templates/index.html:146\nmsgid \"Switch language\"\nmsgstr \"Changer de langue\"\n\n#: templates/index.html:149\nmsgid \"English\"\nmsgstr \"Anglais\"\n\n#: templates/index.html:152\nmsgid \"German\"\nmsgstr \"Allemand\"\n\n#: templates/index.html:155\nmsgid \"French\"\nmsgstr \"Français\"\n\n#: templates/index.html:158\nmsgid \"Brazilian Portuguese\"\nmsgstr \"Brésilien Portugais\"\n\n#: templates/index.html:161\nmsgid \"Misc\"\nmsgstr \"Divers\"\n\n#: templates/index.html:164\nmsgid \"Logout\"\nmsgstr \"Déconnexion\"\n\n#: templates/login.html:21\nmsgid \"Login\"\nmsgstr \"Connexion\"\n\n#: templates/login.html:35\n#, python-format\nmsgid \"Login with your %(backend)s account\"\nmsgstr \"Connectez-vous avec votre compte %(backend)s\"\n"
  },
  {
    "path": "jukebox/jukebox_web/locale/fr/LC_MESSAGES/djangojs.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same license as the PACKAGE package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: PACKAGE VERSION\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2011-11-19 14:24+0100\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: Jens Nistler <opensource@jensnistler.de>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"Language: \\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\n#: static/js/music.js:1\nmsgid \"function\"\nmsgstr \"fontion\"\n\n#: static/js/music.js:105 static/js/music.js.py:106 static/js/music.js:414\n#: static/js/music.js.py:436 static/js/music.js:458 static/js/music.js.py:475\nmsgid \"Revoke vote\"\nmsgstr \"Retirer le vote\"\n\n#: static/js/music.js:122 static/js/music.js.py:123 static/js/music.js:439\n#: static/js/music.js.py:461 static/js/music.js:478\nmsgid \"Vote to play\"\nmsgstr \"Voter à jouer\"\n\n#: static/js/music.js:140 static/js/music.js.py:141 static/js/music.js:417\nmsgid \"Support vote\"\nmsgstr \"Supporter le vote\"\n\n#: static/js/music.js:170 static/js/music.js.py:171 static/js/music.js:420\n#: static/js/music.js.py:442 static/js/music.js:463 static/js/music.js.py:481\nmsgid \"Remove from favourites\"\nmsgstr \"Retirer des favoris\"\n\n#: static/js/music.js:186 static/js/music.js.py:187 static/js/music.js:423\n#: static/js/music.js.py:445 static/js/music.js:484\nmsgid \"Add to favourites\"\nmsgstr \"Ajouter aux favoris\"\n\n#: static/js/music.js:286\nmsgid \"No data found\"\nmsgstr \"Aucune donnée trouvée\"\n\n#: static/js/music.js:351 static/js/music.js.py:359 static/js/music.js:367\n#: static/js/music.js.py:375 static/js/music.js:388\nmsgid \"Title\"\nmsgstr \"Titre\"\n\n#: static/js/music.js:352 static/js/music.js.py:360 static/js/music.js:368\n#: static/js/music.js.py:376 static/js/music.js:389\nmsgid \"Artist\"\nmsgstr \"Artiste\"\n\n#: static/js/music.js:353 static/js/music.js.py:361 static/js/music.js:369\n#: static/js/music.js.py:377\nmsgid \"Album\"\nmsgstr \"Album\"\n\n#: static/js/music.js:354 static/js/music.js.py:362\nmsgid \"Votes\"\nmsgstr \"Votes\"\n\n#: static/js/music.js:355\nmsgid \"First voted\"\nmsgstr \"Première voté\"\n\n#: static/js/music.js:363 static/js/music.js.py:371\nmsgid \"Date added\"\nmsgstr \"Date d'ajout\"\n\n#: static/js/music.js:370 static/js/music.js.py:378\nmsgid \"Genre\"\nmsgstr \"Genre\"\n\n#: static/js/music.js:379 static/js/music.js.py:397\nmsgid \"Year\"\nmsgstr \"An\"\n\n#: static/js/music.js:380\nmsgid \"Length\"\nmsgstr \"Durée\"\n\n#: static/js/music.js:384 static/js/music.js.py:393\nmsgid \"Name\"\nmsgstr \"Nom\"\n\n#: static/js/music.js:451\nmsgid \"Autoplay\"\nmsgstr \"Lecture automatique\"\n"
  },
  {
    "path": "jukebox/jukebox_web/locale/pt_BR/LC_MESSAGES/django.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same license as the PACKAGE package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: PACKAGE VERSION\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2014-11-04 20:03+0100\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"Language: \\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=2; plural=(n > 1);\\n\"\n\n#: templates/index.html:5 templates/index.html.py:6 templates/login.html:5\n#: templates/login.html.py:6\nmsgid \"Democratic Jukebox - your democratic music player\"\nmsgstr \"Democratic Jukebox - o seu player de música democrático\"\n\n#: templates/index.html:15 templates/login.html:15\nmsgid \"Jukebox logo\"\nmsgstr \"logo do Jukebox\"\n\n#: templates/index.html:18\nmsgid \"Account options\"\nmsgstr \"Opções da Conta\"\n\n#: templates/index.html:25\nmsgid \"Search term\"\nmsgstr \"Buscar palavra\"\n\n#: templates/index.html:28 templates/index.html.py:140\nmsgid \"Search\"\nmsgstr \"Buscar\"\n\n#: templates/index.html:35\nmsgid \"Now playing\"\nmsgstr \"Em execução\"\n\n#: templates/index.html:45\nmsgid \"Navigation\"\nmsgstr \"Navegação\"\n\n#: templates/index.html:50\nmsgid \"Queue\"\nmsgstr \"Fila\"\n\n#: templates/index.html:55\nmsgid \"History\"\nmsgstr \"Histórico\"\n\n#: templates/index.html:60\nmsgid \"My History\"\nmsgstr \"Meu Histórico\"\n\n#: templates/index.html:65\nmsgid \"Favourites\"\nmsgstr \"Favoritos\"\n\n#: templates/index.html:71\nmsgid \"Music\"\nmsgstr \"Música\"\n\n#: templates/index.html:76\nmsgid \"Songs\"\nmsgstr \"Músicas\"\n\n#: templates/index.html:81\nmsgid \"Artists\"\nmsgstr \"Artistas\"\n\n#: templates/index.html:86\nmsgid \"Albums\"\nmsgstr \"Albuns\"\n\n#: templates/index.html:91\nmsgid \"Genres\"\nmsgstr \"Gêneros\"\n\n#: templates/index.html:96\nmsgid \"Years\"\nmsgstr \"Anos\"\n\n#: templates/index.html:109\nmsgid \"Title\"\nmsgstr \"Título\"\n\n#: templates/index.html:113\nmsgid \"Artist\"\nmsgstr \"Artista\"\n\n#: templates/index.html:117\nmsgid \"Album\"\nmsgstr \"Album\"\n\n#: templates/index.html:121\nmsgid \"Genre\"\nmsgstr \"Gênero\"\n\n#: templates/index.html:123\nmsgid \"All genres\"\nmsgstr \"Todos os gêneros\"\n\n#: templates/index.html:130\nmsgid \"Year\"\nmsgstr \"Anos\"\n\n#: templates/index.html:132\nmsgid \"All years\"\nmsgstr \"Todos os anos\"\n\n#: templates/index.html:139\nmsgid \"Reset\"\nmsgstr \"Limpar\"\n\n#: templates/index.html:146\nmsgid \"Switch language\"\nmsgstr \"Alterar lingua\"\n\n#: templates/index.html:149\nmsgid \"English\"\nmsgstr \"Inglês\"\n\n#: templates/index.html:152\nmsgid \"German\"\nmsgstr \"Alemão\"\n\n#: templates/index.html:155\nmsgid \"French\"\nmsgstr \"Francês\"\n\n#: templates/index.html:158\nmsgid \"Brazilian Portuguese\"\nmsgstr \"Português do Brasil\"\n\n#: templates/index.html:161\nmsgid \"Misc\"\nmsgstr \"Outros\"\n\n#: templates/index.html:164\nmsgid \"Logout\"\nmsgstr \"Sair\"\n\n#: templates/login.html:21\nmsgid \"Login\"\nmsgstr \"Acessar\"\n\n#: templates/login.html:35\n#, python-format\nmsgid \"Login with your %(backend)s account\"\nmsgstr \"Acessar com sua conta do %(backend)s\"\n"
  },
  {
    "path": "jukebox/jukebox_web/locale/pt_BR/LC_MESSAGES/djangojs.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same license as the PACKAGE package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: PACKAGE VERSION\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2013-12-19 23:24-0300\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"Language: \\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\n#: static/js/music.js:236 static/js/music.js.c:237 static/js/music.js.c:646\n#: static/js/music.js:707 static/js/music.js.c:767 static/js/music.js.c:813\nmsgid \"Revoke vote\"\nmsgstr \"Revogar Voto\"\n\n#: static/js/music.js:252 static/js/music.js.c:253 static/js/music.js.c:710\n#: static/js/music.js:770 static/js/music.js.c:816\nmsgid \"Vote to play\"\nmsgstr \"Vote para tocar\"\n\n#: static/js/music.js:269 static/js/music.js.c:270 static/js/music.js.c:649\nmsgid \"Support vote\"\nmsgstr \"Voto de suporte\"\n\n#: static/js/music.js:297 static/js/music.js.c:298 static/js/music.js.c:652\n#: static/js/music.js:713 static/js/music.js.c:772 static/js/music.js.c:819\nmsgid \"Remove from favourites\"\nmsgstr \"Remover dos favoritos\"\n\n#: static/js/music.js:312 static/js/music.js.c:313 static/js/music.js.c:655\n#: static/js/music.js:716 static/js/music.js.c:822\nmsgid \"Add to favourites\"\nmsgstr \"Adicionar aos favoritos\"\n\n#: static/js/music.js:466\nmsgid \"No data found\"\nmsgstr \"Nenhuma informação encontrada\"\n\n#: static/js/music.js:572 static/js/music.js.c:581 static/js/music.js.c:590\n#: static/js/music.js:599 static/js/music.js.c:608 static/js/music.js.c:621\nmsgid \"Title\"\nmsgstr \"Título\"\n\n#: static/js/music.js:573 static/js/music.js.c:582 static/js/music.js.c:591\n#: static/js/music.js:600 static/js/music.js.c:609\nmsgid \"Artist\"\nmsgstr \"Artista\"\n\n#: static/js/music.js:574 static/js/music.js.c:583 static/js/music.js.c:592\n#: static/js/music.js:601 static/js/music.js.c:610\nmsgid \"Album\"\nmsgstr \"Album\"\n\n#: static/js/music.js:575 static/js/music.js.c:584 static/js/music.js.c:593\nmsgid \"Votes\"\nmsgstr \"Votos\"\n\n#: static/js/music.js:576\nmsgid \"First voted\"\nmsgstr \"Primeiro voto\"\n\n#: static/js/music.js:585 static/js/music.js.c:594 static/js/music.js.c:603\nmsgid \"Date added\"\nmsgstr \"Data de adição\"\n\n#: static/js/music.js:602 static/js/music.js.c:611\nmsgid \"Genre\"\nmsgstr \"Gênero\"\n\n#: static/js/music.js:612 static/js/music.js.c:629\nmsgid \"Year\"\nmsgstr \"Ano\"\n\n#: static/js/music.js:613\nmsgid \"Length\"\nmsgstr \"Tamanho\"\n\n#: static/js/music.js:617 static/js/music.js.c:625\nmsgid \"Name\"\nmsgstr \"Nome\"\n\n#: static/js/music.js:696 static/js/music.js.c:757\nmsgid \"Autoplay\"\nmsgstr \"Tocar automaticamente\"\n"
  },
  {
    "path": "jukebox/jukebox_web/static/css/music.css",
    "content": "body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, form, fieldset, input, textarea, p, blockquote, th, td, button {\n    margin: 0;\n    padding: 0;\n}\n\ntable {\n    border-collapse: collapse;\n    border-spacing: 0;\n    width: 100%;\n}\n\nfieldset, img {\n    border: 0;\n}\n\naddress, caption, cite, code, dfn, em, strong, th, var {\n    font-style: normal;\n    font-weight: normal;\n}\n\nol, ul {\n    list-style: none;\n}\n\ncaption, th {\n    text-align: left;\n}\n\nh1, h2, h3, h4, h5, h6 {\n    font-size: 100%;\n    font-weight: normal;\n}\n\nlabel {\n    cursor: pointer;\n}\n\nq:before, q:after {\n    content: '';\n}\n\nabbr, acronym {\n    border: 0;\n}\n\nstrong {\n    font-weight: bold;\n}\n\nhr {\n    display: block;\n    margin: 10px 0;\n    border: 0;\n    border-top: 1px dotted #999999;\n}\n\nblockquote {\n    margin: 10px 5px;\n    padding: 0 10px;\n    border-left: 1px solid #3a8bcc;\n}\n\na, a:hover, a:visited {\n    color: #ED9005;\n    text-decoration: none;\n}\n\n.invisible {\n    display: none;\n}\n\nbody {\n    color: #444444;\n    font-size: 9pt;\n    font-family: verdana, helvetica, sans-serif;\n    line-height: 150%;\n}\n\n/*\n------------------------------------------------------------------------------------\n header\n------------------------------------------------------------------------------------\n*/\n\n#header {\n    height: 60px;\n    background-color: #F5F5F5;\n    border-bottom: 1px solid #E5E5E5;\n    position: fixed;\n    top: 0;\n    left: 0;\n    z-index: 350;\n    width: 100%;\n}\n\n#logo {\n    position: relative;\n    float: left;\n    width: 220px;\n    padding: 10px 0 0 10px;\n}\n\n#logo span.author {\n    position: absolute;\n    top: 38px;\n    left: 107px;\n    font-size: 8pt;\n}\n\n#logo span.author span.grey {\n    color: #444444;\n}\n\n#search {\n    position: relative;\n    margin-left: 220px;\n}\n\n#search div.searchbox {\n    position: absolute;\n    top: 15px;\n}\n\n#search h1 {\n    position: absolute;\n    top: 22px;\n    left: 28px;\n    font-size: 18pt;\n    font-weight: bold;\n}\n\n#search input.searchterm {\n    position: absolute;\n    left: 25px;\n    height: 30px;\n    width: 330px;\n    margin: 0 !important;\n    padding: 3px 22px 3px 8px;\n    font-family: verdana, helvetica, sans-serif;\n\n    border: 1px solid #D9D9D9 !important;\n    border-top: 1px solid silver !important;\n    border-radius: 1px;\n    -webkit-border-radius: 1px;\n    -moz-border-radius: 1px;\n\n    box-sizing: border-box;\n    -moz-box-sizing: border-box;\n    -webkit-box-sizing: border-box;\n\n    background-color: #FFFFFF;\n}\n\n#searchoptions {\n    position: absolute;\n    left: 335px;\n}\n\nspan.options {\n    height: 4px;\n    width: 7px;\n    padding: 13px 6px;\n    cursor: pointer;\n    background: url(\"../img/arrow_down.png\") no-repeat center;\n}\n\nspan.options:hover, #profile:hover span.options {\n    background: url(\"../img/arrow_down_active.png\") no-repeat center;\n}\n\nspan.searchsubmit {\n    display: block;\n    height: 27px;\n    width: 54px;\n    padding: 0 8px;\n\n    background-color: #F07F09;\n    background: -webkit-linear-gradient(top,#EFA73D,#ED9005);\n    background: -moz-linear-gradient(top,#EFA73D,#ED9005);\n    background: -ms-linear-gradient(top,#EFA73D,#ED9005);\n    background: -o-linear-gradient(top,#EFA73D,#ED9005);\n    background: linear-gradient(top,#EFA73D,#ED9005);\n\n    border: 1px solid #964E06;\n    -webkit-border-radius: 2px;\n    -moz-border-radius: 2px;\n    border-radius: 2px;\n    outline: 0;\n\n    font-size: 8pt;\n    line-height: 27px;\n    font-weight: bold;\n    text-align: center;\n    color: white;\n    cursor: pointer;\n}\n\nspan.searchreset {\n    display: block;\n    height: 27px;\n    width: 54px;\n    padding: 0 8px;\n    float: right;\n\n    background-color: #AAAAAA;\n    background: -webkit-linear-gradient(top,#AAAAAA,#888888);\n    background: -moz-linear-gradient(top,#AAAAAA,#888888);\n    background: -ms-linear-gradient(top,#AAAAAA,#888888);\n    background: -o-linear-gradient(top,#AAAAAA,#888888);\n    background: linear-gradient(top,#AAAAAA,#888888);\n\n    border: 1px solid #444444;\n    -webkit-border-radius: 2px;\n    -moz-border-radius: 2px;\n    border-radius: 2px;\n    outline: 0;\n\n    font-size: 8pt;\n    line-height: 27px;\n    font-weight: bold;\n    text-align: center;\n    color: white;\n    cursor: pointer;\n}\n\n#search span.searchsubmit {\n    position: absolute;\n    left: 370px;\n    margin-right: 16px;\n}\n\n#searchdetails {\n    display: none;\n\n    position: absolute;\n    top: 44px;\n    left: 245px;\n    background-color: #FFFFFF;\n\n    width: 318px;\n    border: 1px solid #CCC;\n    padding: 5px;\n\n    z-index: 400;\n}\n\n#searchdetails label {\n    display: block;\n    font-size: 8pt;\n    margin-bottom: 3px;\n}\n\n#searchdetails input[type=text], #searchdetails select {\n    width: 100%;\n    padding: 3px;\n    font-family: verdana, helvetica, sans-serif;\n\n    border: 1px solid #D9D9D9 !important;\n    border-top: 1px solid silver !important;\n    border-radius: 1px;\n    -webkit-border-radius: 1px;\n    -moz-border-radius: 1px;\n\n    box-sizing: border-box;\n    -moz-box-sizing: border-box;\n    -webkit-box-sizing: border-box;\n}\n\n#searchdetails div.option {\n    margin-bottom: 10px;\n}\n\n#currentSong {\n    position: relative;\n    text-align: left;\n    margin: 12px 120px 0 680px;\n}\n\n#currentSong strong {\n    display: none;\n}\n\n#profile {\n    float: right;\n    margin: 20px 20px 0 0;\n    text-align: right;\n    cursor: pointer;\n}\n\n#profile span.username {\n    float: left;\n    margin-right: 5px;\n}\n\n#accountoptions {\n    display: none;\n\n    position: absolute;\n    top: 44px;\n    right: 20px;\n    background-color: #FFFFFF;\n\n    width: 200px;\n    border: 1px solid #CCC;\n    padding: 15px 5px;\n\n    z-index: 400;\n}\n\n#accountoptions h1 {\n    font-size: 11pt;\n    padding-left: 10px;\n}\n\n#accountoptions ul {\n    margin: 5px 0 15px 0;\n}\n\n#accountoptions ul li {\n    padding: 0 0 5px 20px;\n    border-left:  3px solid #FFFFFF;\n}\n\n#accountoptions ul li.active {\n    font-weight: bold;\n    border-left:  3px solid #ED9005;\n}\n\n/*\n------------------------------------------------------------------------------------\n content\n------------------------------------------------------------------------------------\n*/\n\n#content {\n    margin-top: 75px;\n}\n\n#sidebar {\n    width: 180px;\n    margin-right: 10px;\n    position: fixed;\n    top: 76px;\n    left: 0;\n    z-index: 350;\n}\n\n#sidebar h1 {\n    font-size: 12pt;\n    padding-left: 20px;\n}\n\n#sidebar ul {\n    margin: 5px 0 15px 0;\n}\n\n#sidebar ul li {\n    padding: 0 0 5px 35px;\n    border-left:  3px solid #FFFFFF;\n}\n\n#sidebar ul li.second {\n    padding: 0 0 5px 55px;\n}\n\n#sidebar ul li.active {\n    font-weight: bold;\n    border-left:  3px solid #ED9005;\n}\n\n#main_spacer {\n    position: fixed;\n    top: 61px;\n    left: 250px;\n    right: 30px;\n    height: 15px;\n    width: 100%;\n    background-color: #FFFFFF;\n    z-index: 350;\n}\n\n#main {\n    position: absolute;\n    top: 76px;\n    left: 200px;\n    right: 30px;\n    padding-bottom: 15px;\n}\n\n#main div.login {\n    margin-top: 30px;\n}\n\n#main div.login div {\n    margin-bottom: 30px;\n}\n\n#main p.login_error {\n    color: #FF0000;\n}\n\n#main div.noContent {\n\n}\n\n#main table.list {\n    width: 100%;\n}\n\n#main table.list th {\n    padding:  0 0 10px 0;\n    cursor: pointer;\n}\n\n#main table.list th:hover {\n    color: #ED9005;\n}\n\n#main table.list th.sort_asc:after {\n    content: \" ↗\";\n}\n\n#main table.list th.sort_desc:after {\n    content: \" ↘\";\n}\n\n#main table.list th.options {\n    width: 6%;\n    min-width: 80px;\n}\n\n#main table.list th.song_title, #main table.list th.favourite_title {\n    width: 27%;\n}\n\n#main table.list th.song_artist, #main table.list th.favourite_artist {\n    width: 22%;\n}\n\n#main table.list th.song_album, #main table.list th.favourite_album {\n    width: 20%;\n}\n\n#main table.list th.favourite_added {\n    width: 15%;\n}\n\n#main table.list th.song_year {\n    width: 7%;\n}\n\n#main table.list th.song_length {\n    width: 8%;\n}\n\n#main table.list th.song_genre, #main table.list th.favourite_genre {\n    width: 10%\n}\n\n#main table.list th.options_small {\n    width: 3%;\n}\n\n#main table.list th.name, #main table.list th.year {\n    width: 97%;\n}\n\n#main table.list th.album_title {\n    width: 49%\n}\n\n#main table.list th.album_artist {\n    width: 48%;\n}\n\n#main table.list td {\n    height: 34px;\n    font-size: 9pt;\n    background-color: #F5F5F5;\n    border-bottom: 1px solid #E5E5E5;\n    padding:  5px 0 5px 15px;\n    overflow: hidden;\n}\n\n#main table.list td div.voteTooltip {\n    display: none;\n    position: fixed;\n    border: 1px solid #CCC;\n    background-color: #FFFFFF;\n    padding: 5px;\n    z-index: 400;\n}\n\n#main table.list td.filter {\n    cursor: pointer;\n}\n\n#main table.list td.filter:hover {\n    background-color: #EFA73D;\n}\n\n#main table.list td img {\n    margin-right: 10px;\n    cursor: pointer;\n}\n"
  },
  {
    "path": "jukebox/jukebox_web/static/js/music.js",
    "content": "if (typeof gettext != \"function\") {\n    gettext = function(identifier) {\n        return identifier;\n    }\n}\n\nMusic = {\n    pageNum: 1,\n    hasNextPage: true,\n    options: {},\n    searchOptions: {},\n    infiniteScrollActive: false,\n    sessionPing: 60000,\n    remaining: 0,\n    csrf_token: null,\n\n    init: function() {\n        Music.csrf_token = $('#csrf_token input[name=\"csrfmiddlewaretoken\"]').val();\n        jQuery.ajaxSetup({\n            headers: {\n                'X-CSRFToken': Music.csrf_token\n            }\n        });\n\n        $(\".loadList\").bind(\"click\", function() {\n            Music.options = {};\n            Music.setActiveMenu($(this));\n            Music.loadList($(this).attr(\"href\"));\n            return false;\n        });\n\n        $(\"#searchform span.searchsubmit\").bind(\"click\", function() {\n            Music.options = {\"search_term\": $(\"input.searchterm\").val()};\n            Music.loadList(\"/api/v1/songs\");\n            Music.setActiveMenu($(\"#sidebar ul li a.loadSongs\"));\n            return false;\n        });\n        $(\"#searchform\").bind(\"submit\", function() {\n            Music.options = {\"search_term\": $(\"input.searchterm\").val()};\n            Music.loadList(\"/api/v1/songs\");\n            Music.setActiveMenu($(\"#sidebar ul li a.loadSongs\"));\n            return false;\n        });\n\n        $(\"#searchdetailsform span.searchsubmit\").bind(\"click\", function() {\n            Music.options = Music.getSearchOptions();\n            Music.loadList(\"/api/v1/songs\");\n            Music.setActiveMenu($(\"#sidebar ul li a.loadSongs\"));\n            $(\"#searchoptions\").click();\n            return false;\n        });\n        $(\"#searchdetailsform span.searchreset\").bind(\"click\", function() {\n            $(\"#search_title\").val(\"\");\n            $(\"#search_artist\").val(\"\");\n            $(\"#search_album\").val(\"\");\n            $(\"#search_genre\").val(\"\");\n            $(\"#search_year\").val(\"\");\n            return false;\n        });\n        $(\"#searchdetailsform\").bind(\"submit\", function() {\n            Music.options = Music.getSearchOptions();\n            Music.loadList(\"/api/v1/songs\");\n            Music.setActiveMenu($(\"#sidebar ul li a.loadSongs\"));\n            $(\"#searchoptions\").click();\n            return false;\n        });\n        // chrome doesn't fire submit on #searchdetailsform -> dirty workaround\n        $(\"#search_title, #search_artist, #search_album, #search_album\").bind(\"keydown\", function() {\n            if (event.which == 13) {\n                Music.options = Music.getSearchOptions();\n                Music.loadList(\"/api/v1/songs\");\n                Music.setActiveMenu($(\"#sidebar ul li a.loadSongs\"));\n                $(\"#searchoptions\").click();\n                return false;\n            }\n        });\n\n        $(\"#searchoptions\").bind(\"click\", function() {\n            if ($(\"#searchdetails:visible\").length == 1) {\n                $(document).unbind(\"click.search\");\n                $(\"#searchdetails\").hide();\n            }\n            else {\n                // reset all\n                $(\"#searchdetailsform span.searchreset\").click();\n\n                // fill form by current search options\n                $.each(Music.searchOptions, function(key, value) {\n                    if (key != \"genre_id\" && value.substr(0, 1) == \"(\") {\n                        value = value.substr(1, value.length - 2);\n                    }\n                    switch (key) {\n                        case \"title\":\n                            $(\"#search_title\").val(value);\n                            break;\n                        case \"artist\":\n                            $(\"#search_artist\").val(value);\n                            break;\n                        case \"album\":\n                            $(\"#search_album\").val(value);\n                            break;\n                        case \"genre_id\":\n                            $(\"#search_genre\").val(value);\n                            break;\n                        case \"year\":\n                            $(\"#search_year\").val(value);\n                            break;\n                    }\n                });\n\n                $(\"#searchdetails\").show();\n                $(document).bind(\"click.search\", function(event) {\n                    if ($(event.target).closest(\"#searchdetails\").length == 0 && $(event.target).closest(\"#searchoptions\").length == 0) {\n                        $(\"#searchoptions\").click();\n                    }\n                });\n            }\n        });\n\n        $(\"#profile\").bind(\"click\", function() {\n            if ($(\"#accountoptions:visible\").length == 1) {\n                $(document).unbind(\"click.account\");\n                $(\"#accountoptions\").hide();\n            }\n            else {\n                $(\"#accountoptions\").show();\n                $(document).bind(\"click.account\", function(event) {\n                    if ($(event.target).closest(\"#accountoptions\").length == 0 && $(event.target).closest(\"#profile\").length == 0) {\n                        $(\"#profile\").click();\n                    }\n                });\n            }\n        });\n\n        $(\"#main table.list td.filter\").live(\"click\", function() {\n            var value = $(this).attr(\"data-value\");\n            if ($(this).hasClass(\"filter_artist\")) {\n                Music.options = {\"filter_artist_id\": value};\n            }\n            else if ($(this).hasClass(\"filter_album\")) {\n                Music.options = {\"filter_album_id\": value};\n            }\n            else if ($(this).hasClass(\"filter_genre\")) {\n                Music.options = {\"filter_genre\": value};\n            }\n            else if ($(this).hasClass(\"filter_year\")) {\n                Music.options = {\"filter_year\": value};\n            }\n            else if ($(this).hasClass(\"search_title\")) {\n                Music.options = {\"search_title\": value};\n            }\n            Music.loadList(\"/api/v1/songs\");\n            Music.setActiveMenu($(\"#sidebar ul li a.loadSongs\"));\n            return false;\n        });\n\n        $(\"#main table.list th\").live(\"click\", function() {\n            if ($(this).hasClass(\"sort_asc\")) {\n                Music.options.order_direction = \"desc\";\n            }\n            else {\n                Music.options.order_direction = \"asc\";\n            }\n\n            if ($(this).hasClass(\"sort_title\")) {\n                Music.options.order_by = \"title\";\n            }\n            else if ($(this).hasClass(\"sort_artist\")) {\n                Music.options.order_by = \"artist\";\n            }\n            else if ($(this).hasClass(\"sort_album\")) {\n                Music.options.order_by = \"album\";\n            }\n            else if ($(this).hasClass(\"sort_genre\")) {\n                Music.options.order_by = \"genre\";\n            }\n            else if ($(this).hasClass(\"sort_year\")) {\n                Music.options.order_by = \"year\";\n            }\n            else if ($(this).hasClass(\"sort_length\")) {\n                Music.options.order_by = \"length\";\n            }\n            else if ($(this).hasClass(\"sort_created\")) {\n                Music.options.order_by = \"created\";\n            }\n            else if ($(this).hasClass(\"sort_votes\")) {\n                Music.options.order_by = \"votes\";\n            }\n\n            switch ($(this).closest(\"tr\").attr(\"class\")) {\n                case \"queue\":\n                    Music.loadList(\"/api/v1/queue\");\n                    break;\n                case \"history\":\n                    Music.loadList(\"/api/v1/history\");\n                    break;\n                case \"history_my\":\n                    Music.loadList(\"/api/v1/history/my\");\n                    break;\n                case \"favourites\":\n                    Music.loadList(\"/api/v1/favourites\");\n                    break;\n                case \"songs\":\n                    Music.loadList(\"/api/v1/songs\");\n                    break;\n                case \"artists\":\n                    Music.loadList(\"/api/v1/artists\");\n                    break;\n                case \"albums\":\n                    Music.loadList(\"/api/v1/albums\");\n                    break;\n                case \"genres\":\n                    Music.loadList(\"/api/v1/genres\");\n                    break;\n                case \"years\":\n                    Music.loadList(\"/api/v1/years\");\n                    break;\n            }\n\n            return false;\n        });\n\n        $(\"#main table.list img.queue_add\").live(\"click\", function() {\n            $.ajax({\n                url: \"/api/v1/queue\",\n                type: \"POST\",\n                data: {\n                    \"id\": $(this).attr(\"data-id\")\n                },\n                success: function(data) {\n                    var item = $(\"img.queue_add[data-id=\" + data.id + \"]\");\n                    item.attr(\"src\", \"/static/img/queue_active.png\");\n                    item.removeClass(\"queue_add\");\n                    item.addClass(\"queue_remove\");\n                    item.attr(\"id\", item.attr(\"id\").replace(/add/, \"remove\"));\n                    item.attr(\"alt\", gettext(\"Revoke vote\"));\n                    item.attr(\"title\", gettext(\"Revoke vote\"));\n                    item.closest(\"tr\").find(\".voteCount\").html(data.count);\n                }\n            });\n            return false;\n        });\n\n        $(\"#main table.list img.queue_remove\").live(\"click\", function() {\n            var success = null;\n            if ($(this).closest(\"tr.row_queue\").length == 0) {\n                success = function(data) {\n                    var item = $(\"img.queue_remove[data-id=\" + data.id + \"]\");\n                    item.attr(\"src\", \"/static/img/queue.png\");\n                    item.removeClass(\"queue_remove\");\n                    item.addClass(\"queue_add\");\n                    item.attr(\"alt\", gettext(\"Vote to play\"));\n                    item.attr(\"title\", gettext(\"Vote to play\"));\n                };\n            }\n            else {\n                success = function(data) {\n                    var item = $(\"img.queue_remove[data-id=\" + data.id + \"]\");\n\n                    if (data.count == 0) {\n                        item.closest(\"tr\").fadeOut(1000, function() {\n                            item.closest(\"tr\").remove();\n                        });\n                        return;\n                    }\n                    item.attr(\"src\", \"/static/img/queue.png\");\n                    item.removeClass(\"queue_remove\");\n                    item.addClass(\"queue_add\");\n                    item.attr(\"alt\", gettext(\"Support vote\"));\n                    item.attr(\"title\", gettext(\"Support vote\"));\n                    item.closest(\"tr\").find(\".voteCount\").html(data.count);\n                };\n            }\n\n            $.ajax({\n                url: \"/api/v1/queue/\" + $(this).attr(\"data-id\"),\n                type: \"DELETE\",\n                success: function(data) {\n                    success(data);\n                }\n            });\n            return false;\n        });\n\n        $(\"#main table.list img.favourite_add\").live(\"click\", function() {\n            $.ajax({\n                url: \"/api/v1/favourites\",\n                type: \"POST\",\n                data: {\n                    \"id\": $(this).attr(\"data-id\")\n                },\n                success: function(data) {\n                    var item = $(\"img.favourite_add[data-id=\" + data.id + \"]\");\n                    item.attr(\"src\", \"/static/img/favourite_active.png\");\n                    item.removeClass(\"favourite_add\");\n                    item.addClass(\"favourite_remove\");\n                    item.attr(\"alt\", gettext(\"Remove from favourites\"));\n                    item.attr(\"title\", gettext(\"Remove from favourites\"));\n                }\n            });\n            return false;\n        });\n\n        $(\"#main table.list img.favourite_remove\").live(\"click\", function() {\n            var success = null;\n            if ($(this).closest(\"tr.row_favourites\").length == 0) {\n                success = function(data) {\n                    var item = $(\"img.favourite_remove[data-id=\" + data.id + \"]\");\n                    item.attr(\"src\", \"/static/img/favourite.png\");\n                    item.removeClass(\"favourite_remove\");\n                    item.addClass(\"favourite_add\");\n                    item.attr(\"alt\", gettext(\"Add to favourites\"));\n                    item.attr(\"title\", gettext(\"Add to favourites\"));\n                };\n            }\n            else {\n                success = function(data) {\n                    var item = $(\"img.favourite_remove[data-id=\" + data.id + \"]\");\n                    item.closest(\"tr\").fadeOut(1000, function() {\n                        item.closest(\"tr\").remove();\n                    });\n                };\n            }\n\n            $.ajax({\n                url: \"/api/v1/favourites/\" + $(this).attr(\"data-id\"),\n                type: \"DELETE\",\n                success: function(data) {\n                    success(data);\n                }\n            });\n            return false;\n        });\n\n        $(\"td.voteCount\").live({\n            mouseenter: function() {\n                var element = $(this);\n                var tooltip = $(this).find('div.voteTooltip');\n                if (tooltip.length == 1) {\n                    tooltip.css('left', element.offset().left - $(window).scrollLeft() + 50);\n                    tooltip.css('top', element.offset().top - $(window).scrollTop() + 10);\n                    tooltip.show();\n                }\n            },\n            mouseleave: function() {\n                var tooltip = $(this).find('div.voteTooltip');\n                if (tooltip.length == 1) {\n                    tooltip.hide();\n                }\n            }\n        });\n\n        $.ajaxSetup({\n            type: \"GET\",\n            cache: false,\n            dataType: \"json\"\n        });\n\n        Music.getCurrentSong();\n        Music.ping();\n        Music.loadList(\"/api/v1/queue\");\n        Music.setActiveMenu($(\"#sidebar ul li a.loadQueue\"));\n    },\n\n    ping: function() {\n        $.ajax({\n            url: \"/api/v1/ping\",\n            success: function() {\n                setTimeout(\"Music.ping()\", Music.sessionPing);\n            }\n        });\n    },\n\n    getCurrentSong: function() {\n         $.ajax({\n            url: \"/api/v1/songs/current\",\n            success: function(data) {\n                if (\"id\" in data) {\n                    $('#currentSong strong').show();\n                    $('#currentSong span.songTitle').html(data.artist.name + \" - \" + data.title);\n                    Music.remaining = data.remaining;\n                    Music.updateTimeLeft();\n                }\n                else {\n                    $('#currentSong strong').hide();\n                }\n            }\n        });\n    },\n\n    updateTimeLeft: function() {\n        if (0 == Music.remaining) {\n            $('#currentSong span.timeRemaining').hide();\n            setTimeout(\"Music.getCurrentSong()\", 1000);\n            return;\n        }\n        else if (Music.remaining > 0) {\n            var minutes = parseInt(Music.remaining / 60);\n            var secondsInt = Music.remaining % 60;\n            var seconds = secondsInt.toString();\n            if (secondsInt < 10) {\n                seconds = \"0\" + seconds;\n            }\n            $('#currentSong span.timeRemaining').show();\n            $('#currentSong span.timeRemaining').html(\"(\" + minutes + \":\" + seconds + \")\");\n            Music.remaining--;\n        }\n        setTimeout(\"Music.updateTimeLeft()\", 1000);\n    },\n\n    getSearchOptions: function() {\n        var options = {};\n\n        var value = $(\"#search_title\").val();\n        if (value.length > 0) {\n            options.search_title = value;\n        }\n        var value = $(\"#search_artist\").val();\n        if (value.length > 0) {\n            options.search_artist = value;\n        }\n        var value = $(\"#search_album\").val();\n        if (value.length > 0) {\n            options.search_album = value;\n        }\n        var item = $(\"#search_genre\").find(\"option:selected\");\n        if (item.length > 0 && item.val().length > 0) {\n            options.filter_genre = item.val();\n        }\n        var item = $(\"#search_year\").find(\"option:selected\");\n        if (item.length > 0 && item.val().length > 0) {\n            options.filter_year = item.val();\n        }\n\n        return options;\n    },\n\n    setActiveMenu: function(item) {\n        $(\"#sidebar\").find(\"li\").removeClass(\"active\");\n        item.closest(\"li\").addClass(\"active\");\n    },\n\n    loadList: function(url) {\n        Music.pageNum = 1;\n        Music.hasNextPage = false;\n        Music.searchOptions = {};\n        $(\"#searchdetailsform span.searchreset\").click();\n\n        // build options\n        Music.options.page = Music.pageNum;\n        Music.options.count = 30;\n\n        $.ajax({\n            url: url,\n            data: Music.options,\n            success: function(data) {\n                $(window).unbind(\"scroll\");\n                $(window).scrollTop(0);\n\n                Music.hasNextPage = data.hasNextPage;\n                if (data.itemList.length > 0) {\n                    $(\"#main\").html(Music.renderTable(data));\n                    $(\"#main table.list tbody\").append(Music.renderData(data));\n                }\n                else {\n                    $(\"#main\").html(\"<div class=\\\"noContent\\\">\" + gettext(\"No data found\") + \"</div>\");\n                }\n\n                // set search term - don't iterate to get correct order\n                Music.searchOptions = data.search;\n                terms = [];\n                if (data.search.title) {\n                    terms.push(\"title:\" + data.search.title);\n                }\n                if (data.search.artist) {\n                    terms.push(\"artist:\" + data.search.artist);\n                }\n                if (data.search.album) {\n                    terms.push(\"album:\" + data.search.album);\n                }\n                if (data.search.genre) {\n                    terms.push(\"genre:\" + data.search.genre);\n                }\n                if (data.search.year) {\n                    terms.push(\"year:\" + data.search.year);\n                }\n                if (data.search.term) {\n                    terms.push(data.search.term);\n                }\n                $(\"input.searchterm\").val(terms.join(\" \"));\n\n                // load data until page is populated\n                if (Music.hasNextPage && Music.getScrollHeight() <= $(document).height()) {\n                    $(window).unbind(\"scroll\");\n                    Music.loadItems(url, Music.options);\n                }\n            }\n        });\n    },\n\n    loadOnScroll: function(event) {\n        if (Music.infiniteScrollActive === true) {\n            return false;\n        }\n\n        if (Music.hasNextPage && Music.getScrollHeight() > Music.getDocumentHeight()) {\n            $(window).unbind(\"scroll\");\n            Music.loadItems(event.data.url, event.data.options);\n        }\n    },\n\n    getScrollHeight: function() {\n        return $(window).scrollTop() + $(window).height();\n    },\n\n    getDocumentHeight: function() {\n        return $(document).height() - $(document).height() * 0.2;\n    },\n\n    loadItems: function(url, options) {\n        if (Music.hasNextPage === false) {\n            return false;\n        }\n\n        if (typeof options == \"undefined\") {\n            options = {};\n        }\n\n        Music.infiniteScrollActive = true;\n        Music.pageNum = Music.pageNum + 1;\n\n        options.page = Music.pageNum;\n        options.count = 30;\n\n        $.ajax({\n            url: url.replace(/\\[page\\]/, Music.pageNum),\n            data: options,\n            success: function(data) {\n                Music.hasNextPage = data.hasNextPage;\n                $(\"#main table.list tbody\").append(Music.renderData(data));\n\n                if (Music.hasNextPage) {\n                    $(window).bind(\"scroll\", {url: url, options: options}, Music.loadOnScroll);\n                }\n                Music.infiniteScrollActive = false;\n            }\n        });\n    },\n\n    getOrderClass: function(field, data) {\n        for (var key in data.order) {\n            var item = data.order[key];\n            if (item.field == field) {\n                switch (item.direction) {\n                    case \"asc\":\n                        return \" sort_asc\";\n                    case \"desc\":\n                        return \" sort_desc\";\n                }\n            }\n        }\n        return \"\";\n    },\n\n    renderTable: function(data) {\n        var html = \"<table class=\\\"list\\\"><thead>\";\n\n        switch (data.type) {\n            case \"queue\":\n                html+= \"<tr class=\\\"queue\\\">\";\n                html+= \"<th class=\\\"options\\\">&#160;</th>\";\n                html+= \"<th class=\\\"favourite_title sort_title\" + Music.getOrderClass(\"title\", data) + \"\\\">\" + gettext(\"Title\") + \"</th>\";\n                html+= \"<th class=\\\"favourite_artist sort_artist\" + Music.getOrderClass(\"artist\", data) + \"\\\">\" + gettext(\"Artist\") + \"</th>\";\n                html+= \"<th class=\\\"favourite_album sort_album\" + Music.getOrderClass(\"album\", data) + \"\\\">\" + gettext(\"Album\") + \"</th>\";\n                html+= \"<th class=\\\"favourite_genre sort_votes\" + Music.getOrderClass(\"votes\", data) + \"\\\">\" + gettext(\"Votes\") + \"</th>\";\n                html+= \"<th class=\\\"favourite_added sort_created\" + Music.getOrderClass(\"created\", data) + \"\\\">\" + gettext(\"First voted\") + \"</th>\";\n                break;\n            case \"history\":\n                html+= \"<tr class=\\\"history\\\">\";\n                html+= \"<th class=\\\"options\\\">&#160;</th>\";\n                html+= \"<th class=\\\"favourite_title sort_title\" + Music.getOrderClass(\"title\", data) + \"\\\">\" + gettext(\"Title\") + \"</th>\";\n                html+= \"<th class=\\\"favourite_artist sort_artist\" + Music.getOrderClass(\"artist\", data) + \"\\\">\" + gettext(\"Artist\") + \"</th>\";\n                html+= \"<th class=\\\"favourite_album sort_album\" + Music.getOrderClass(\"album\", data) + \"\\\">\" + gettext(\"Album\") + \"</th>\";\n                html+= \"<th class=\\\"favourite_genre sort_genre\" + Music.getOrderClass(\"votes\", data) + \"\\\">\" + gettext(\"Votes\") + \"</th>\";\n                html+= \"<th class=\\\"favourite_added sort_created\" + Music.getOrderClass(\"created\", data) + \"\\\">\" + gettext(\"Date added\") + \"</th>\";\n                break;\n            case \"history/my\":\n                html+= \"<tr class=\\\"history_my\\\">\";\n                html+= \"<th class=\\\"options\\\">&#160;</th>\";\n                html+= \"<th class=\\\"favourite_title sort_title\" + Music.getOrderClass(\"title\", data) + \"\\\">\" + gettext(\"Title\") + \"</th>\";\n                html+= \"<th class=\\\"favourite_artist sort_artist\" + Music.getOrderClass(\"artist\", data) + \"\\\">\" + gettext(\"Artist\") + \"</th>\";\n                html+= \"<th class=\\\"favourite_album sort_album\" + Music.getOrderClass(\"album\", data) + \"\\\">\" + gettext(\"Album\") + \"</th>\";\n                html+= \"<th class=\\\"favourite_genre sort_genre\" + Music.getOrderClass(\"votes\", data) + \"\\\">\" + gettext(\"Votes\") + \"</th>\";\n                html+= \"<th class=\\\"favourite_added sort_created\" + Music.getOrderClass(\"created\", data) + \"\\\">\" + gettext(\"Date added\") + \"</th>\";\n                break;\n            case \"favourites\":\n                html+= \"<tr class=\\\"favourites\\\">\";\n                html+= \"<th class=\\\"options\\\">&#160;</th>\";\n                html+= \"<th class=\\\"favourite_title sort_title\" + Music.getOrderClass(\"title\", data) + \"\\\">\" + gettext(\"Title\") + \"</th>\";\n                html+= \"<th class=\\\"favourite_artist sort_artist\" + Music.getOrderClass(\"artist\", data) + \"\\\">\" + gettext(\"Artist\") + \"</th>\";\n                html+= \"<th class=\\\"favourite_album sort_album\" + Music.getOrderClass(\"album\", data) + \"\\\">\" + gettext(\"Album\") + \"</th>\";\n                html+= \"<th class=\\\"favourite_genre sort_genre\" + Music.getOrderClass(\"genre\", data) + \"\\\">\" + gettext(\"Genre\") + \"</th>\";\n                html+= \"<th class=\\\"favourite_added sort_created\" + Music.getOrderClass(\"created\", data) + \"\\\">\" + gettext(\"Date added\") + \"</th>\";\n                break;\n            case \"songs\":\n                html+= \"<tr class=\\\"songs\\\">\";\n                html+= \"<th class=\\\"options\\\">&#160;</th>\";\n                html+= \"<th class=\\\"song_title sort_title\" + Music.getOrderClass(\"title\", data) + \"\\\">\" + gettext(\"Title\") + \"</th>\";\n                html+= \"<th class=\\\"song_artist sort_artist\" + Music.getOrderClass(\"artist\", data) + \"\\\">\" + gettext(\"Artist\") + \"</th>\";\n                html+= \"<th class=\\\"song_album sort_album\" + Music.getOrderClass(\"album\", data) + \"\\\">\" + gettext(\"Album\") + \"</th>\";\n                html+= \"<th class=\\\"song_genre sort_genre\" + Music.getOrderClass(\"genre\", data) + \"\\\">\" + gettext(\"Genre\") + \"</th>\";\n                html+= \"<th class=\\\"song_year sort_year\" + Music.getOrderClass(\"year\", data) + \"\\\">\" + gettext(\"Year\") + \"</th>\";\n                html+= \"<th class=\\\"song_length sort_length\" + Music.getOrderClass(\"length\", data) + \"\\\">\" + gettext(\"Length\") + \"</th>\";\n                break;\n            case \"artists\":\n                html+= \"<tr class=\\\"artists\\\">\";\n                html+= \"<th class=\\\"name sort_artist\" + Music.getOrderClass(\"artist\", data) + \"\\\">\" + gettext(\"Name\") + \"</th>\";\n                break;\n            case \"albums\":\n                html+= \"<tr class=\\\"albums\\\">\";\n                html+= \"<th class=\\\"album_title sort_album\" + Music.getOrderClass(\"album\", data) + \"\\\">\" + gettext(\"Title\") + \"</th>\";\n                break;\n            case \"genres\":\n                html+= \"<tr class=\\\"genres\\\">\";\n                html+= \"<th class=\\\"name sort_genre\" + Music.getOrderClass(\"genre\", data) + \"\\\">\" + gettext(\"Name\") + \"</th>\";\n                break;\n            case \"years\":\n                html+= \"<tr class=\\\"years\\\">\";\n                html+= \"<th class=\\\"year sort_year\" + Music.getOrderClass(\"year\", data) + \"\\\">\" + gettext(\"Year\") + \"</th>\";\n                break;\n        }\n        $(window).bind(\"scroll\", {url: \"/api/v1/\" + data.type}, Music.loadOnScroll);\n        html+= \"</tr></thead><tbody></tbody></table>\";\n\n        return html;\n    },\n\n    renderData: function(data) {\n        var html = \"\";\n        $.each(data.itemList, function(index, item) {\n            switch (data.type) {\n                case \"queue\":\n                    html+= \"<tr class=\\\"row_queue\\\">\";\n                    html+= \"<td>\";\n                    if (item.queued) {\n                        html+= \"<img src=\\\"/static/img/queue_active.png\\\" class=\\\"queue_remove\\\" data-id=\\\"\" + item.id + \"\\\" alt=\\\"\" + gettext(\"Revoke vote\") + \"\\\" title=\\\"\" + gettext(\"Revoke vote\") + \"\\\" />\";\n                    }\n                    else {\n                        html+= \"<img src=\\\"/static/img/queue.png\\\" class=\\\"queue_add\\\" data-id=\\\"\" + item.id + \"\\\" alt=\\\"\" + gettext(\"Support vote\") + \"\\\" title=\\\"\" + gettext(\"Support vote\") + \"\\\" />\";\n                    }\n                    if (item.favourite) {\n                        html+= \"<img src=\\\"/static/img/favourite_active.png\\\" class=\\\"favourite_remove\\\" data-id=\\\"\" + item.id + \"\\\" alt=\\\"\" + gettext(\"Remove from favourites\") + \"\\\" title=\\\"\" + gettext(\"Remove from favourites\") + \"\\\" />\";\n                    }\n                    else {\n                        html+= \"<img src=\\\"/static/img/favourite.png\\\" class=\\\"favourite_add\\\" data-id=\\\"\" + item.id + \"\\\" alt=\\\"\" + gettext(\"Add to favourites\") + \"\\\" title=\\\"\" + gettext(\"Add to favourites\") + \"\\\" />\";\n                    }\n                    html+= \"</td>\";\n\n                    // title\n                    if (item.title != null) {\n                        html+= \"<td class=\\\"filter search_title\\\" data-value=\\\"\" + item.title + \"\\\">\" + item.title + \"</td>\";\n                    }\n                    else {\n                        html+= \"<td>&#160;</td>\";\n                    }\n\n                    // artist\n                    if (item.artist.id != null) {\n                        html+= \"<td class=\\\"filter filter_artist\\\" data-value=\\\"\" + item.artist.id + \"\\\">\" + item.artist.name + \"</td>\";\n                    }\n                    else {\n                        html+= \"<td>&#160;</td>\";\n                    }\n\n                    // album\n                    if (item.album.id != null) {\n                        html+= \"<td class=\\\"filter filter_album\\\" data-value=\\\"\" + item.album.id + \"\\\">\" + item.album.title + \"</td>\";\n                    }\n                    else {\n                        html+= \"<td>&#160;</td>\";\n                    }\n\n                    html+= \"<td class=\\\"voteCount\\\">\";\n                    if (item.votes > 0) {\n                        if (item.users.length == item.votes) {\n                            html+= \"<div class=\\\"voteTooltip\\\"><ul>\";\n                            for (var key in item.users) {\n                                var user = item.users[key];\n                                html+= \"<li>\" + user.name + \"</li>\"\n                            }\n                            html+= \"</ul></div>\";\n                        }\n                        html+= item.votes;\n                    }\n                    else {\n                        html+= gettext(\"Autoplay\");\n                    }\n                    html+= \"</td>\";\n\n                    html+= \"<td>\" + item.created + \"</td>\";\n                    break;\n                case \"history\":\n                case \"history/my\":\n                    html+= \"<tr class=\\\"row_history\\\">\";\n                    html+= \"<td>\";\n                    if (item.queued) {\n                        html+= \"<img src=\\\"/static/img/queue_active.png\\\" class=\\\"queue_remove\\\" data-id=\\\"\" + item.id + \"\\\" alt=\\\"\" + gettext(\"Revoke vote\") + \"\\\" title=\\\"\" + gettext(\"Revoke vote\") + \"\\\" />\";\n                    }\n                    else {\n                        html+= \"<img src=\\\"/static/img/queue.png\\\" class=\\\"queue_add\\\" data-id=\\\"\" + item.id + \"\\\" alt=\\\"\" + gettext(\"Vote to play\") + \"\\\" title=\\\"\" + gettext(\"Vote to play\") + \"\\\" />\";\n                    }\n                    if (item.favourite) {\n                        html+= \"<img src=\\\"/static/img/favourite_active.png\\\" class=\\\"favourite_remove\\\" data-id=\\\"\" + item.id + \"\\\" alt=\\\"\" + gettext(\"Remove from favourites\") + \"\\\" title=\\\"\" + gettext(\"Remove from favourites\") + \"\\\" />\";\n                    }\n                    else {\n                        html+= \"<img src=\\\"/static/img/favourite.png\\\" class=\\\"favourite_add\\\" data-id=\\\"\" + item.id + \"\\\" alt=\\\"\" + gettext(\"Add to favourites\") + \"\\\" title=\\\"\" + gettext(\"Add to favourites\") + \"\\\" />\";\n                    }\n                    html+= \"</td>\";\n\n                    // title\n                    if (item.title != null) {\n                        html+= \"<td class=\\\"filter search_title\\\" data-value=\\\"\" + item.title + \"\\\">\" + item.title + \"</td>\";\n                    }\n                    else {\n                        html+= \"<td>&#160;</td>\";\n                    }\n\n                    // artist\n                    if (item.artist.id != null) {\n                        html+= \"<td class=\\\"filter filter_artist\\\" data-value=\\\"\" + item.artist.id + \"\\\">\" + item.artist.name + \"</td>\";\n                    }\n                    else {\n                        html+= \"<td>&#160;</td>\";\n                    }\n\n                    // album\n                    if (item.album.id != null) {\n                        html+= \"<td class=\\\"filter filter_album\\\" data-value=\\\"\" + item.album.id + \"\\\">\" + item.album.title + \"</td>\";\n                    }\n                    else {\n                        html+= \"<td>&#160;</td>\";\n                    }\n\n                    html+= \"<td class=\\\"voteCount\\\">\";\n                    if (item.votes > 0) {\n                        if (item.users.length == item.votes) {\n                            html+= \"<div class=\\\"voteTooltip\\\"><ul>\";\n                            for (var key in item.users) {\n                                var user = item.users[key];\n                                html+= \"<li>\" + user.name + \"</li>\"\n                            }\n                            html+= \"</ul></div>\";\n                        }\n                        html+= item.votes;\n                    }\n                    else {\n                        html+= gettext(\"Autoplay\");\n                    }\n                    html+= \"</td>\";\n\n                    html+= \"<td>\" + item.created + \"</td>\";\n                    break;\n                case \"favourites\":\n                    html+= \"<tr class=\\\"row_favourites\\\">\";\n                    html+= \"<td>\";\n                    if (item.queued) {\n                        html+= \"<img src=\\\"/static/img/queue_active.png\\\" class=\\\"queue_remove\\\" data-id=\\\"\" + item.id + \"\\\" alt=\\\"\" + gettext(\"Revoke vote\") + \"\\\" title=\\\"\" + gettext(\"Revoke vote\") + \"\\\" />\";\n                    }\n                    else {\n                        html+= \"<img src=\\\"/static/img/queue.png\\\" class=\\\"queue_add\\\" data-id=\\\"\" + item.id + \"\\\" alt=\\\"\" + gettext(\"Vote to play\") + \"\\\" title=\\\"\" + gettext(\"Vote to play\") + \"\\\" />\";\n                    }\n                    html+= \"<img src=\\\"/static/img/favourite_active.png\\\" class=\\\"favourite_remove\\\" data-id=\\\"\" + item.id + \"\\\" alt=\\\"\" + gettext(\"Remove from favourites\") + \"\\\" title=\\\"\" + gettext(\"Remove from favourites\") + \"\\\" />\";\n                    html+= \"</td>\";\n\n                    // title\n                    if (item.title != null) {\n                        html+= \"<td class=\\\"filter search_title\\\" data-value=\\\"\" + item.title + \"\\\">\" + item.title + \"</td>\";\n                    }\n                    else {\n                        html+= \"<td>&#160;</td>\";\n                    }\n\n                    // artist\n                    if (item.artist.id != null) {\n                        html+= \"<td class=\\\"filter filter_artist\\\" data-value=\\\"\" + item.artist.id + \"\\\">\" + item.artist.name + \"</td>\";\n                    }\n                    else {\n                        html+= \"<td>&#160;</td>\";\n                    }\n\n                    // album\n                    if (item.album.id != null) {\n                        html+= \"<td class=\\\"filter filter_album\\\" data-value=\\\"\" + item.album.id + \"\\\">\" + item.album.title + \"</td>\";\n                    }\n                    else {\n                        html+= \"<td>&#160;</td>\";\n                    }\n\n                    // genre\n                    if (item.genre.id != null) {\n                        html+= \"<td class=\\\"filter filter_genre\\\" data-value=\\\"\" + item.genre.id + \"\\\">\" + item.genre.name + \"</td>\";\n                    }\n                    else {\n                        html+= \"<td>&#160;</td>\";\n                    }\n\n                    html+= \"<td>\" + item.created + \"</td>\";\n                    break;\n                case \"songs\":\n                    html+= \"<tr class=\\\"row_songs\\\">\";\n                    html+= \"<td>\";\n                    if (item.queued) {\n                        html+= \"<img src=\\\"/static/img/queue_active.png\\\" class=\\\"queue_remove\\\" data-id=\\\"\" + item.id + \"\\\" alt=\\\"\" + gettext(\"Revoke vote\") + \"\\\" title=\\\"\" + gettext(\"Revoke vote\") + \"\\\" />\";\n                    }\n                    else {\n                        html+= \"<img src=\\\"/static/img/queue.png\\\" class=\\\"queue_add\\\" data-id=\\\"\" + item.id + \"\\\" alt=\\\"\" + gettext(\"Vote to play\") + \"\\\" title=\\\"\" + gettext(\"Vote to play\") + \"\\\" />\";\n                    }\n                    if (item.favourite) {\n                        html+= \"<img src=\\\"/static/img/favourite_active.png\\\" class=\\\"favourite_remove\\\" data-id=\\\"\" + item.id + \"\\\" alt=\\\"\" + gettext(\"Remove from favourites\") + \"\\\" title=\\\"\" + gettext(\"Remove from favourites\") + \"\\\" />\";\n                    }\n                    else {\n                        html+= \"<img src=\\\"/static/img/favourite.png\\\" class=\\\"favourite_add\\\" data-id=\\\"\" + item.id + \"\\\" alt=\\\"\" + gettext(\"Add to favourites\") + \"\\\" title=\\\"\" + gettext(\"Add to favourites\") + \"\\\" />\";\n                    }\n                    html+= \"</td>\";\n                    // html+= \"<td>\" + ((item.title != null) ?  : \"\")  + \"<span class=\\\"value invisible\\\">\" + item.id + \"</span></td>\";\n\n                    // title\n                    if (item.title != null) {\n                        html+= \"<td class=\\\"filter search_title\\\" data-value=\\\"\" + item.title + \"\\\">\" + item.title + \"</td>\";\n                    }\n                    else {\n                        html+= \"<td>&#160;</td>\";\n                    }\n\n                    // artist\n                    if (item.artist.id != null) {\n                        html+= \"<td class=\\\"filter filter_artist\\\" data-value=\\\"\" + item.artist.id + \"\\\">\" + item.artist.name + \"</td>\";\n                    }\n                    else {\n                        html+= \"<td>&#160;</td>\";\n                    }\n\n                    // album\n                    if (item.album.id != null) {\n                        html+= \"<td class=\\\"filter filter_album\\\" data-value=\\\"\" + item.album.id + \"\\\">\" + item.album.title + \"</td>\";\n                    }\n                    else {\n                        html+= \"<td>&#160;</td>\";\n                    }\n\n                    // genre\n                    if (item.genre.id != null) {\n                        html+= \"<td class=\\\"filter filter_genre\\\" data-value=\\\"\" + item.genre.id + \"\\\">\" + item.genre.name + \"</td>\";\n                    }\n                    else {\n                        html+= \"<td>&#160;</td>\";\n                    }\n\n                    // year\n                    if (item.year != null) {\n                        html+= \"<td class=\\\"filter filter_year\\\" data-value=\\\"\" + item.year + \"\\\">\" + item.year + \"</td>\";\n                    }\n                    else {\n                        html+= \"<td>&#160;</td>\";\n                    }\n\n                    // length\n                    var minutes = parseInt(item.length / 60);\n                    var seconds = item.length % 60;\n                    if (seconds < 10) {\n                        seconds = \"0\" + seconds;\n                    }\n                    html+= \"<td>\" + minutes + \":\" + seconds + \"</td>\";\n                    break;\n                case \"artists\":\n                    html+= \"<tr class=\\\"row_artists\\\">\";\n                    html+= \"<td class=\\\"filter filter_artist\\\" data-value=\\\"\" + item.id + \"\\\">\" + item.artist + \"</td>\";\n                    break;\n                case \"albums\":\n                    html+= \"<tr class=\\\"row_albums\\\">\";\n                    html+= \"<td class=\\\"filter filter_album\\\" data-value=\\\"\" + item.id + \"\\\">\" + item.album + \"</td>\";\n                    break;\n                case \"genres\":\n                    html+= \"<tr class=\\\"row_genres\\\">\";\n                    html+= \"<td class=\\\"filter filter_genre\\\" data-value=\\\"\" + item.id + \"\\\">\" + item.genre + \"</td>\";\n                    break;\n                case \"years\":\n                    html+= \"<tr class=\\\"row_years\\\">\";\n                    html+= \"<td class=\\\"filter filter_year\\\" data-value=\\\"\" + item.year + \"\\\">\" + item.year + \"</td>\";\n                    break;\n            }\n            html+= \"</tr>\";\n        });\n\n        return html;\n    }\n};\n\n$(document).ready(function() {\n    Music.init();\n});\n"
  },
  {
    "path": "jukebox/jukebox_web/templates/index.html",
    "content": "{% load i18n %}\n<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\n    <head>\n        <title>{% trans \"Democratic Jukebox - your democratic music player\" %}</title>\n        <meta name=\"description\" content=\"{% trans 'Democratic Jukebox - your democratic music player' %}\">\n        <meta charset=\"UTF-8\"/>\n        <link rel=\"shortcut icon\" href=\"/static/img/favicon.ico\" type=\"image/x-icon\"/>\n        <link type=\"text/css\" rel=\"stylesheet\" href=\"/static/css/music.css\" />\n    </head>\n    <body>\n        <div id=\"header\">\n            <div id=\"logo\">\n                <a href=\"/\">\n                    <img src=\"/static/img/jukebox.png\" alt=\"{% trans 'Jukebox logo' %}\" width=\"156\" height=\"40\" />\n                </a>\n            </div>\n            <div id=\"profile\" title=\"{% trans 'Account options' %}\">\n                <span class=\"username\">{{ username }}</span>\n                <span class=\"options\"></span>\n            </div>\n            <div id=\"search\">\n                <div class=\"searchbox\">\n                    <form action=\"/api/v1/songs\" method=\"get\" id=\"searchform\">\n                        <label for=\"term\" class=\"invisible\">{% trans 'Search term' %}</label>\n                        <input type=\"text\" name=\"term\" id=\"term\" maxlength=\"100\" class=\"searchterm\" />\n                        <span id=\"searchoptions\" class=\"options\"></span>\n                        <span class=\"searchsubmit\">{% trans 'Search' %}</span>\n                        <button type=\"submit\" class=\"invisible\"></button>\n                    </form>\n                </div>\n            </div>\n            <div id=\"currentSong\">\n                <strong>\n                    {% trans 'Now playing' %}\n                </strong>\n                <br />\n                <span class=\"songTitle\"></span>\n                <span class=\"timeRemaining\"></span>\n            </div>\n        </div>\n        <div id=\"content\">\n            <div id=\"sidebar\">\n                <h1>\n                    {% trans 'Navigation' %}\n                </h1>\n                <ul>\n                    <li>\n                        <a href=\"/api/v1/queue\" class=\"loadList loadQueue\">\n                            {% trans 'Queue' %}\n                        </a>\n                    </li>\n                    <li>\n                        <a href=\"/api/v1/history\" class=\"loadList loadHistory\">\n                            {% trans 'History' %}\n                        </a>\n                    </li>\n                    <li class=\"second\">\n                        <a href=\"/api/v1/history/my\" class=\"loadList loadMyHistory\">\n                            {% trans 'My History' %}\n                        </a>\n                    </li>\n                    <li>\n                        <a href=\"/api/v1/favourites\" class=\"loadList loadFavourite\">\n                            {% trans 'Favourites' %}\n                        </a>\n                    </li>\n                </ul>\n\n                <h1>\n                    {% trans \"Music\" %}\n                </h1>\n                <ul>\n                    <li>\n                        <a href=\"/api/v1/songs\" class=\"loadList loadSongs\">\n                            {% trans 'Songs' %}\n                        </a>\n                    </li>\n                    <li>\n                        <a href=\"/api/v1/artists\" class=\"loadList loadArtists\">\n                            {% trans 'Artists' %}\n                        </a>\n                    </li>\n                    <li>\n                        <a href=\"/api/v1/albums\" class=\"loadList loadAlbums\">\n                            {% trans 'Albums' %}\n                        </a>\n                    </li>\n                    <li>\n                        <a href=\"/api/v1/genres\" class=\"loadList loadGenres\">\n                            {% trans 'Genres' %}\n                        </a>\n                    </li>\n                    <li>\n                        <a href=\"/api/v1/years\" class=\"loadList loadYears\">\n                            {% trans 'Years' %}\n                        </a>\n                    </li>\n                </ul>\n            </div>\n            <div id=\"main_spacer\"></div>\n            <div id=\"main\">\n\n            </div>\n        </div>\n        <div id=\"searchdetails\">\n            <form action=\"/api/v1/songs\" method=\"get\" id=\"searchdetailsform\">\n                <div class=\"option\">\n                    <label for=\"search_title\">{% trans 'Title' %}:</label>\n                    <input type=\"text\" name=\"search_title\" id=\"search_title\" />\n                </div>\n                <div class=\"option\">\n                    <label for=\"search_artist\">{% trans 'Artist' %}:</label>\n                    <input type=\"text\" name=\"search_artist\" id=\"search_artist\" />\n                </div>\n                <div class=\"option\">\n                    <label for=\"search_album\">{% trans 'Album' %}:</label>\n                    <input type=\"text\" name=\"search_album\" id=\"search_album\" />\n                </div>\n                <div class=\"option\">\n                    <label for=\"search_genre\">{% trans 'Genre' %}:</label>\n                    <select name=\"search_genre\" id=\"search_genre\">\n                        <option value=\"\">{% trans \"All genres\" %}</option>\n                        {% for genre in genres %}\n                            <option value=\"{{ genre.id }}\">{{ genre.Name }}</option>\n                        {% endfor %}\n                    </select>\n                </div>\n                <div class=\"option\">\n                    <label for=\"search_year\">{% trans 'Year' %}:</label>\n                    <select name=\"search_year\" id=\"search_year\">\n                        <option value=\"\">{% trans 'All years' %}</option>\n                        {% for year in years %}\n                            <option value=\"{{ year.Year }}\">{{ year.Year }}</option>\n                        {% endfor %}\n                    </select>\n                </div>\n                <div class=\"option\">\n                    <span class=\"searchreset\">{% trans 'Reset' %}</span>\n                    <span class=\"searchsubmit\">{% trans 'Search' %}</span>\n                    <button type=\"submit\" class=\"invisible\"></button>\n                </div>\n            </form>\n        </div>\n        <div id=\"accountoptions\">\n            <h1>{%  trans 'Switch language' %}</h1>\n            <ul>\n                <li>\n                    <a href=\"/language/set/en\">{% trans 'English' %}</a>\n                </li>\n                <li>\n                    <a href=\"/language/set/de\">{% trans 'German' %}</a>\n                </li>\n                <li>\n                    <a href=\"/language/set/fr\">{% trans 'French' %}</a>\n                </li>\n                <li>\n                    <a href=\"/language/set/pt-br\">{% trans 'Brazilian Portuguese' %}</a>\n                </li>\n            </ul>\n            <h1>{% trans 'Misc' %}</h1>\n            <ul>\n                <li>\n                    <a href=\"/logout\">{% trans 'Logout' %}</a>\n                </li>\n            </ul>\n        </div>\n        <div id=\"csrf_token\">\n            {% csrf_token %}\n        </div>\n        <script type=\"text/javascript\" src=\"/static/js/jquery-1.7.min.js\"></script>\n        <script type=\"text/javascript\" src=\"{% url django.views.i18n.javascript_catalog %}\"></script>\n        <script type=\"text/javascript\" src=\"/static/js/music.js\"></script>\n    </body>\n</html>\n"
  },
  {
    "path": "jukebox/jukebox_web/templates/login.html",
    "content": "{% load i18n %}\n<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\n    <head>\n        <title>{% trans 'Democratic Jukebox - your democratic music player' %}</title>\n        <meta name=\"description\" content=\"{% trans 'Democratic Jukebox - your democratic music player' %}\">\n        <meta charset=\"UTF-8\"/>\n        <link rel=\"shortcut icon\" href=\"/static/img/favicon.ico\" type=\"image/x-icon\"/>\n        <link type=\"text/css\" rel=\"stylesheet\" href=\"/static/css/music.css\" />\n    </head>\n    <body>\n        <div id=\"header\">\n            <div id=\"logo\">\n                <a href=\"/\">\n                    <img src=\"/static/img/jukebox.png\" alt=\"{% trans 'Jukebox logo' %}\" width=\"156\" height=\"40\" />\n                </a>\n            </div>\n            <div id=\"profile\">\n            </div>\n            <div id=\"search\">\n                <h1>{% trans 'Login' %}</h1>\n            </div>\n        </div>\n        <div id=\"content\">\n            <div id=\"main\">\n                {% if error %}\n                    {% for msg in error %}\n                        <p class=\"login_error\">{{ msg.message }}</p>\n                    {% endfor %}\n                {% endif %}\n                <div class=\"login\">\n                    {% for backend in backends %}\n                        <div>\n                            <a rel=\"nofollow\" href=\"{% url socialauth_begin backend %}\">\n                                <img src=\"/static/img/{{ backend }}.png\" alt=\"{% blocktrans %}Login with your {{ backend }} account{% endblocktrans %}\" title=\"{% blocktrans %}Login with your {{ backend }} account{% endblocktrans %}\" />\n                            </a>\n                        </div>\n                    {%  endfor %}\n                </div>\n            </div>\n        </div>\n    </body>\n</html>\n"
  },
  {
    "path": "jukebox/jukebox_web/urls.py",
    "content": "# -*- coding: UTF-8 -*-\n\nfrom django.conf.urls import patterns, url\nfrom jukebox.jukebox_core.models import QueueFeed\nimport views\n\njs_info_dict = {\n    'packages': (\n        'jukebox_web',\n    ),\n}\n\nurlpatterns = patterns(\"\",\n    url(r\"^$\", views.index, name=\"jukebox_web_index\"),\n    url(r\"^login$\", views.login, name=\"jukebox_web_login\"),\n    url(r\"^login/error$\", views.login_error, name=\"jukebox_web_login_error\"),\n    url(\n        r\"^language/set/(?P<language>[-a-z]{5}|[a-z]{2})\",\n        views.language,\n        name=\"jukebox_web_language\"\n    ),\n    url(r\"^logout$\", views.logout, name=\"jukebox_web_logout\"),\n    url(r'^jsi18n/$', 'django.views.i18n.javascript_catalog', js_info_dict),\n\n     # RSS feed url\n    (r'^feed/$', QueueFeed()),\n)\n"
  },
  {
    "path": "jukebox/jukebox_web/views.py",
    "content": "# -*- coding: UTF-8 -*-\n\nfrom django.shortcuts import render_to_response\nfrom django.core.context_processors import csrf\nfrom django.http import HttpResponseRedirect\nfrom django.contrib.auth import logout as auth_logout\nfrom django.template import RequestContext\nfrom django.contrib.messages.api import get_messages\nfrom django.conf import settings\nfrom jukebox.jukebox_core.models import Song, Genre\n\ndef index(request):\n    if request.user.is_authenticated():\n        request.session.set_expiry(settings.SESSION_TTL)\n\n        genres = Genre.objects.all()\n        years = Song.objects.values(\"Year\").distinct()\n        years = years.exclude(Year=None).exclude(Year=0).order_by(\"Year\")\n\n        context = {\n            \"username\": request.user.get_full_name(),\n            \"genres\": genres,\n            \"years\": years\n        }\n        context.update(csrf(request))\n        return render_to_response('index.html', context)\n    else:\n        return HttpResponseRedirect('login')\n\ndef login(request):\n    if request.user.is_authenticated():\n        return HttpResponseRedirect('index')\n    else:\n        return render_to_response(\n            'login.html',\n            {\n                \"backends\": settings.SOCIAL_AUTH_ENABLED_BACKENDS,\n            },\n            RequestContext(request)\n        )\n\ndef login_error(request):\n    messages = get_messages(request)\n    return render_to_response(\n        'login.html',\n        {\"error\": messages},\n        RequestContext(request)\n    )\n\ndef logout(request):\n    auth_logout(request)\n    return HttpResponseRedirect('/')\n\ndef language(request, language):\n    from django.utils.translation import check_for_language\n    from django.utils import translation\n\n    response = HttpResponseRedirect(\"/\")\n    if language and check_for_language(language):\n        if hasattr(request, \"session\"):\n            request.session[\"django_language\"] = language\n        else:\n            response.set_cookie(settings.LANGUAGE_COOKIE_NAME, language)\n        translation.activate(language)\n\n    return response\n"
  },
  {
    "path": "jukebox/manage.py",
    "content": "#!/usr/bin/env python\nfrom django.core.management import execute_manager\nimport imp\ntry:\n    imp.find_module('settings') # Assumed to be in the same directory.\nexcept ImportError:\n    import sys\n    sys.stderr.write(\"Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\\nYou'll have to run django-admin.py, passing it your settings module.\\n\" % __file__)\n    sys.exit(1)\n\nimport settings\n\nif __name__ == \"__main__\":\n    execute_manager(settings)\n"
  },
  {
    "path": "jukebox/settings.py",
    "content": "import os\nimport pkgutil\nimport sys\n\nBASE_DIR = os.path.normpath(os.path.dirname(__file__))\n\nDEBUG = False\nTEMPLATE_DEBUG = DEBUG\n\nADMINS = ()\nMANAGERS = ADMINS\n\nJUKEBOX_STORAGE_PATH = os.path.join(\n    os.path.expanduser('~'),\n    '.jukebox',\n)\nif not os.path.exists(JUKEBOX_STORAGE_PATH):\n    try:\n        os.makedirs(JUKEBOX_STORAGE_PATH, 0750)\n    except os.error:\n        JUKEBOX_STORAGE_PATH = BASE_DIR\n\nDATABASES = {\n    'default': {\n        'ENGINE': 'django.db.backends.sqlite3',\n        'NAME': os.path.join(\n            JUKEBOX_STORAGE_PATH,\n            'db.sqlite'\n        ),\n    }\n}\n\nSITE_ID = 1\n\nTIME_ZONE = 'Europe/Berlin'\nLANGUAGE_CODE = 'en-us'\nLANGUAGES = (\n    ('de', 'Deutsch'),\n    ('en', 'English'),\n    ('fr', 'French'),\n    ('pt-br', 'Brazilian Portuguese'),\n)\nUSE_I18N = True\nUSE_L10N = True\n\nSTATIC_ROOT = os.path.join(BASE_DIR, 'static')\nSTATIC_URL = '/static/'\nMIDDLEWARE_CLASSES = (\n    'django.contrib.sessions.middleware.SessionMiddleware',\n    'django.middleware.locale.LocaleMiddleware',\n    'django.middleware.common.CommonMiddleware',\n    'django.middleware.csrf.CsrfViewMiddleware',\n    'django.contrib.auth.middleware.AuthenticationMiddleware',\n    'django.contrib.messages.middleware.MessageMiddleware',\n)\n\nTEMPLATE_DIRS = (\n    os.path.join(BASE_DIR, 'jukebox_web/templates'),\n)\n\nADMIN_MEDIA_PREFIX = '/static/admin/'\n\nROOT_URLCONF = 'jukebox.urls'\n\nLOCALE_PATHS = (\n    os.path.join(BASE_DIR, 'jukebox_web/locale'),\n)\n\nINSTALLED_APPS = (\n    'django.contrib.auth',\n    'django.contrib.contenttypes',\n    'django.contrib.sessions',\n    'django.contrib.sites',\n    'django.contrib.messages',\n    'django.contrib.staticfiles',\n    'django.contrib.admin',\n    'django.contrib.admindocs',\n    'rest_framework',\n    'social_auth',\n    'south',\n    'jukebox_core',\n    'jukebox_web',\n)\n\n# automatically add jukebox plugins\nfor item in pkgutil.iter_modules():\n    if str(item[1]).startswith('jukebox_'):\n        INSTALLED_APPS += (str(item[1]), )\n\nTEMPLATE_CONTEXT_PROCESSORS = (\n    'django.contrib.auth.context_processors.auth',\n    'django.core.context_processors.debug',\n    'django.core.context_processors.i18n',\n    'django.core.context_processors.media',\n    'django.contrib.messages.context_processors.messages',\n    'social_auth.context_processors.social_auth_by_type_backends',\n)\n\nLOGIN_URL          = '/login'\nLOGIN_ERROR_URL    = '/login/error'\nLOGIN_REDIRECT_URL = '/'\n\nSESSION_TTL = 300\n\nsys.path.append(JUKEBOX_STORAGE_PATH)\ntry:\n    from settings_local import *\nexcept ImportError:\n    pass\n"
  },
  {
    "path": "jukebox/settings_local.example.py",
    "content": "ADMINS = (\n    (\"[admin_user]\", \"[admin_email]\"),\n)\n\nDEBUG = True\nTEMPLATE_DEBUG = DEBUG\n\nSECRET_KEY = \"yourSecretKey\"\n\nAUTHENTICATION_BACKENDS = (\n    \"django.contrib.auth.backends.ModelBackend\",\n    [auth_backends]\n)\n\nSOCIAL_AUTH_ENABLED_BACKENDS = ([auth_backends_enabled])\n\n[auth_data]\n"
  },
  {
    "path": "jukebox/urls.py",
    "content": "# -*- coding: UTF-8 -*-\n\nfrom django.conf.urls import patterns, include, url\nfrom django.contrib import admin\nadmin.autodiscover()\n\nurlpatterns = patterns(\"\",\n    url(r\"^admin/\", include(admin.site.urls)),\n    url(r\"^admin/doc/\", include(\"django.contrib.admindocs.urls\")),\n\n    url(r'', include('jukebox.jukebox_web.urls')),\n    url(r'', include('jukebox.jukebox_core.urls')),\n    url(r'', include('social_auth.urls')),\n)\n"
  },
  {
    "path": "requirements.txt",
    "content": "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",
    "content": "# -*- coding: UTF-8 -*-\nimport glob\nfrom setuptools import setup, find_packages\n\nsetup(\n    name=\"jukebox\",\n    packages=find_packages(),\n    version=\"0.4.1\",\n    description=\"Democratic Jukebox - your democratic music player\",\n    author=\"Jens Nistler\",\n    author_email=\"opensource@jensnistler.de\",\n    url=\"http://jensnistler.de/\",\n    download_url='http://github.com/lociii/jukebox',\n    keywords=[\"jukebox\", \"music\", \"mp3\"],\n    license=\"MIT\",\n    classifiers=[\n        \"Programming Language :: Python\",\n        \"Programming Language :: Python :: 2\",\n        \"Development Status :: 3 - Alpha\",\n        \"Environment :: Web Environment\",\n        \"Intended Audience :: End Users/Desktop\",\n        \"Intended Audience :: Developers\",\n        \"License :: OSI Approved :: MIT License\",\n        \"Operating System :: OS Independent\",\n        \"Topic :: Multimedia :: Sound/Audio :: Players\",\n    ],\n    install_requires=[\n        \"Django==1.4.5\",\n        \"mutagen==1.21\",\n        \"django-social-auth==0.7.20\",\n        \"djangorestframework==2.2.1\",\n        \"simplejson==3.1.0\",\n        \"South==0.7.6\",\n    ],\n    include_package_data=True,\n    scripts=glob.glob(\"bin/*\"),\n    long_description=open(\"README.rst\").read()\n)\n"
  }
]