Reorganize code.
authorRadek Czajka <rczajka@rczajka.pl>
Thu, 28 Feb 2019 09:27:12 +0000 (10:27 +0100)
committerRadek Czajka <rczajka@rczajka.pl>
Thu, 28 Feb 2019 09:27:12 +0000 (10:27 +0100)
88 files changed:
.gitignore [changed mode: 0755->0644]
apps/archive/__init__.py [deleted file]
apps/archive/admin.py [deleted file]
apps/archive/constants.py [deleted file]
apps/archive/cover.png [deleted file]
apps/archive/forms.py [deleted file]
apps/archive/locale/pl/LC_MESSAGES/django.mo [deleted file]
apps/archive/locale/pl/LC_MESSAGES/django.po [deleted file]
apps/archive/migrations/0001_initial.py [deleted file]
apps/archive/migrations/0002_auto__del_field_audiobook_publishing_tags__del_field_audiobook_publish.py [deleted file]
apps/archive/migrations/0003_auto__add_field_audiobook_source_sha1__add_field_audiobook_translator.py [deleted file]
apps/archive/migrations/0004_auto__chg_field_audiobook_source_file.py [deleted file]
apps/archive/migrations/0005_auto__add_field_audiobook_part_name__add_field_audiobook_index__add_fi.py [deleted file]
apps/archive/migrations/__init__.py [deleted file]
apps/archive/models.py [deleted file]
apps/archive/settings.py [deleted file]
apps/archive/static/style.css [deleted file]
apps/archive/tasks.py [deleted file]
apps/archive/templates/404.html [deleted file]
apps/archive/templates/500.html [deleted file]
apps/archive/templates/archive/base.html [deleted file]
apps/archive/templates/archive/file_managed.html [deleted file]
apps/archive/templates/archive/file_new.html [deleted file]
apps/archive/templates/archive/file_unmanaged.html [deleted file]
apps/archive/templates/archive/list.html [deleted file]
apps/archive/templates/archive/list_new.html [deleted file]
apps/archive/templates/archive/list_published.html [deleted file]
apps/archive/templates/archive/list_publishing.html [deleted file]
apps/archive/templates/archive/list_unmanaged.html [deleted file]
apps/archive/templates/archive/list_unpublished.html [deleted file]
apps/archive/templates/archive/tags/multiple_tags_table.html [deleted file]
apps/archive/templates/archive/tags/tags_table.html [deleted file]
apps/archive/templates/base.html [deleted file]
apps/archive/templates/registration/login.html [deleted file]
apps/archive/templatetags/__init__.py [deleted file]
apps/archive/templatetags/tags.py [deleted file]
apps/archive/tests.py [deleted file]
apps/archive/urls.py [deleted file]
apps/archive/utils.py [deleted file]
apps/archive/views.py [deleted file]
audiobooks/__init__.py [deleted file]
audiobooks/manage.py [deleted file]
audiobooks/settings.py [deleted file]
audiobooks/urls.py [deleted file]
audiobooks/wsgi.py [deleted file]
src/archive/__init__.py [new file with mode: 0644]
src/archive/admin.py [new file with mode: 0644]
src/archive/constants.py [new file with mode: 0644]
src/archive/cover.png [new file with mode: 0644]
src/archive/forms.py [new file with mode: 0644]
src/archive/locale/pl/LC_MESSAGES/django.mo [new file with mode: 0644]
src/archive/locale/pl/LC_MESSAGES/django.po [new file with mode: 0644]
src/archive/migrations/0001_initial.py [new file with mode: 0644]
src/archive/migrations/0002_auto__del_field_audiobook_publishing_tags__del_field_audiobook_publish.py [new file with mode: 0644]
src/archive/migrations/0003_auto__add_field_audiobook_source_sha1__add_field_audiobook_translator.py [new file with mode: 0644]
src/archive/migrations/0004_auto__chg_field_audiobook_source_file.py [new file with mode: 0644]
src/archive/migrations/0005_auto__add_field_audiobook_part_name__add_field_audiobook_index__add_fi.py [new file with mode: 0644]
src/archive/migrations/__init__.py [new file with mode: 0644]
src/archive/models.py [new file with mode: 0644]
src/archive/settings.py [new file with mode: 0644]
src/archive/static/style.css [new file with mode: 0755]
src/archive/tasks.py [new file with mode: 0644]
src/archive/templates/404.html [new file with mode: 0644]
src/archive/templates/500.html [new file with mode: 0644]
src/archive/templates/archive/base.html [new file with mode: 0644]
src/archive/templates/archive/file_managed.html [new file with mode: 0755]
src/archive/templates/archive/file_new.html [new file with mode: 0644]
src/archive/templates/archive/file_unmanaged.html [new file with mode: 0755]
src/archive/templates/archive/list.html [new file with mode: 0644]
src/archive/templates/archive/list_new.html [new file with mode: 0644]
src/archive/templates/archive/list_published.html [new file with mode: 0755]
src/archive/templates/archive/list_publishing.html [new file with mode: 0755]
src/archive/templates/archive/list_unmanaged.html [new file with mode: 0755]
src/archive/templates/archive/list_unpublished.html [new file with mode: 0755]
src/archive/templates/archive/tags/multiple_tags_table.html [new file with mode: 0755]
src/archive/templates/archive/tags/tags_table.html [new file with mode: 0755]
src/archive/templates/base.html [new file with mode: 0755]
src/archive/templates/registration/login.html [new file with mode: 0755]
src/archive/templatetags/__init__.py [new file with mode: 0755]
src/archive/templatetags/tags.py [new file with mode: 0755]
src/archive/urls.py [new file with mode: 0644]
src/archive/utils.py [new file with mode: 0644]
src/archive/views.py [new file with mode: 0644]
src/audiobooks/__init__.py [new file with mode: 0644]
src/audiobooks/settings.py [new file with mode: 0644]
src/audiobooks/urls.py [new file with mode: 0644]
src/audiobooks/wsgi.py [new file with mode: 0644]
src/manage.py [new file with mode: 0755]

old mode 100755 (executable)
new mode 100644 (file)
diff --git a/apps/archive/__init__.py b/apps/archive/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/apps/archive/admin.py b/apps/archive/admin.py
deleted file mode 100644 (file)
index d0804be..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-from archive.models import Project, Audiobook
-from django.contrib import admin
-
-admin.site.register(Project)
-admin.site.register(Audiobook)
diff --git a/apps/archive/constants.py b/apps/archive/constants.py
deleted file mode 100644 (file)
index 17f4e78..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-from django.utils.translation import ugettext_lazy as _
-
-class status:
-    WAITING = 1
-    ENCODING = 2
-    TAGGING = 3
-    SENDING = 4
-
-    choices = [
-        (WAITING, _('Waiting')),
-        (ENCODING, _('Encoding')),
-        (TAGGING, _('Tagging')),
-        (SENDING, _('Sending')),
-    ]
diff --git a/apps/archive/cover.png b/apps/archive/cover.png
deleted file mode 100644 (file)
index 1435a03..0000000
Binary files a/apps/archive/cover.png and /dev/null differ
diff --git a/apps/archive/forms.py b/apps/archive/forms.py
deleted file mode 100644 (file)
index 81568c6..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-from datetime import datetime
-import os
-import os.path
-
-from django import forms
-from django.utils.translation import ugettext_lazy as _
-import mutagen
-from django.utils.encoding import force_bytes
-
-from archive.models import Audiobook
-from archive.settings import FILES_PATH, NEW_PATH
-from archive.utils import ExistingFile, sha1_file
-
-class AudiobookForm(forms.ModelForm):
-    class Meta:
-        model = Audiobook
-
-    def save(self, commit=True, path=None):
-        """ Performs normal save, with given file as an source audiobook.
-
-            `path' is relative to NEW_PATH.
-        """
-        m = super(AudiobookForm, self).save(commit=False)
-        m.modified = datetime.now()
-
-        if path:
-            # adding a new audiobook
-            if not os.path.isdir(FILES_PATH):
-                os.makedirs(FILES_PATH)
-            # save the file in model
-
-            abs_path = os.path.join(NEW_PATH, path)
-            m.source_file.save(
-                path,
-                ExistingFile(abs_path))
-
-#            f = open(force_bytes(m.source_file.path))
-#            m.source_sha1 = sha1_file(f)
-#            f.close()
-
-        if commit:
-            m.save()
diff --git a/apps/archive/locale/pl/LC_MESSAGES/django.mo b/apps/archive/locale/pl/LC_MESSAGES/django.mo
deleted file mode 100644 (file)
index e1135bf..0000000
Binary files a/apps/archive/locale/pl/LC_MESSAGES/django.mo and /dev/null differ
diff --git a/apps/archive/locale/pl/LC_MESSAGES/django.po b/apps/archive/locale/pl/LC_MESSAGES/django.po
deleted file mode 100644 (file)
index 317ab76..0000000
+++ /dev/null
@@ -1,250 +0,0 @@
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) 2011 Fundacja Nowoczesna Polska
-# This file is distributed under the same license as the PACKAGE package.
-# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: PACKAGE VERSION\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-03-02 09:36+0100\n"
-"PO-Revision-Date: 2012-07-11 15:52+0100\n"
-"Last-Translator: Radek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>\n"
-"Language-Team: LANGUAGE <LL@li.org>\n"
-"Language: \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
-"|| n%100>=20) ? 1 : 2)\n"
-
-#: constants.py:10
-msgid "Waiting"
-msgstr "W kolejce"
-
-#: constants.py:11
-msgid "Encoding"
-msgstr "Konwersja"
-
-#: constants.py:12
-msgid "Tagging"
-msgstr "Opisywanie"
-
-#: constants.py:13
-msgid "Sending"
-msgstr "WysyÅ‚anie"
-
-#: models.py:23 models.py:49
-msgid "project"
-msgstr "projekt"
-
-#: models.py:24
-msgid "projects"
-msgstr "projekty"
-
-#: models.py:37
-msgid "source file"
-msgstr "plik ÅºródÅ‚owy"
-
-#: models.py:40
-msgid "title"
-msgstr "tytuÅ‚"
-
-#: models.py:41
-msgid "part name"
-msgstr "nazwa części"
-
-#: models.py:41
-msgid "eg. chapter in a novel"
-msgstr "np. rozdziaÅ‚ w powieÅ›ci"
-
-#: models.py:43
-msgid "index"
-msgstr "numer"
-
-#: models.py:44
-msgid "parts count"
-msgstr "liczba części"
-
-#: models.py:45
-msgid "artist"
-msgstr "lektor"
-
-#: models.py:46
-msgid "conductor"
-msgstr "reżyser"
-
-#: models.py:47
-msgid "encoded by"
-msgstr "przyg. techn."
-
-#: models.py:48
-msgid "date"
-msgstr "data"
-
-#: models.py:50
-msgid "book url"
-msgstr "URL książki"
-
-#: models.py:51
-msgid "translator"
-msgstr "tÅ‚umacz"
-
-#: models.py:71
-msgid "audiobook"
-msgstr "audiobook"
-
-#: models.py:72
-msgid "audiobooks"
-msgstr "audiobooki"
-
-#: templates/base.html:7
-msgid "Audiobook repository"
-msgstr "Repozytorium audiobooków"
-
-#: templates/archive/base.html:5
-msgid "New"
-msgstr "Nowe"
-
-#: templates/archive/base.html:6
-msgid "Unpublished"
-msgstr "Nie opublikowane"
-
-#: templates/archive/base.html:7 templates/archive/file_managed.html:11
-msgid "Publishing"
-msgstr "Publikacja"
-
-#: templates/archive/base.html:8
-msgid "Published"
-msgstr "Opublikowane"
-
-#: templates/archive/base.html:9
-msgid "Archive"
-msgstr "Archiwum"
-
-#: templates/archive/base.html:11
-msgid "Projects"
-msgstr "Projekty"
-
-#: templates/archive/base.html:14
-msgid "Logout"
-msgstr "Wyloguj"
-
-#: templates/archive/base.html:16 templates/registration/login.html:9
-msgid "Login"
-msgstr "Zaloguj"
-
-#: templates/archive/base.html:19
-msgid "Administration"
-msgstr "Administracja"
-
-#: templates/archive/file_managed.html:15
-msgid "Publishing pending"
-msgstr "Czeka na publikacjÄ™"
-
-#: templates/archive/file_managed.html:19
-msgid "Cancel publishing"
-msgstr "Anuluj publikacjÄ™"
-
-#: templates/archive/file_managed.html:51
-msgid "Publish"
-msgstr "Opublikuj"
-
-#: templates/archive/file_managed.html:57
-msgid "Convert without publishing"
-msgstr "Konwertuj bez publikacji"
-
-#: templates/archive/file_managed.html:67
-msgid "MP3 file"
-msgstr "Plik MP3"
-
-#: templates/archive/file_managed.html:68
-msgid "Download MP3 file."
-msgstr "Pobierz plik MP3."
-
-#: templates/archive/file_managed.html:70
-#: templates/archive/file_managed.html:86
-msgid "Published:"
-msgstr "Opublikowano:"
-
-#: templates/archive/file_managed.html:75
-#: templates/archive/file_managed.html:91
-msgid "Not published yet."
-msgstr "Nie opublikowane."
-
-#: templates/archive/file_managed.html:78
-msgid "MP3 file hasn't been generated yet."
-msgstr "Plik MP3 nie zostaÅ‚ jeszcze wygenerowany."
-
-#: templates/archive/file_managed.html:83
-msgid "Ogg Vorbis file"
-msgstr "Plik Ogg Vorbis"
-
-#: templates/archive/file_managed.html:84
-msgid "Download Ogg Vorbis file."
-msgstr "Pobierz plik Ogg Vorbis."
-
-#: templates/archive/file_managed.html:94
-msgid "Ogg Vorbis file hasn't been generated yet."
-msgstr "Plik Ogg Vorbis nie zostaÅ‚ jeszcze wygenerowany."
-
-#: templates/archive/file_managed.html:105
-msgid "Update tags"
-msgstr "Uaktualnij tagi"
-
-#: templates/archive/file_managed.html:117 templates/archive/file_new.html:19
-msgid "Commit"
-msgstr "Zatwierdź"
-
-#: templates/archive/file_managed.html:128
-msgid "Are you sure you want to move this audiobook to archive?"
-msgstr "Czy na pewno chcesz przenieść ten plik to archiwum?"
-
-#: templates/archive/file_managed.html:130
-msgid "Remove to archive"
-msgstr "UsuÅ„ do archiwum"
-
-#: templates/archive/file_new.html:8
-msgid "Move to archive"
-msgstr "PrzenieÅ› do archiwum"
-
-#: templates/archive/file_unmanaged.html:6
-msgid "File with same name already exists!"
-msgstr "Plik o tej nazwie już istnieje!"
-
-#: templates/archive/file_unmanaged.html:28
-msgid "Move to new files"
-msgstr "PrzenieÅ› do nowych plików"
-
-#: templates/archive/list_new.html:6
-msgid "New audiobooks"
-msgstr "Nowe audiobooki"
-
-#: templates/archive/list_new.html:11
-msgid "Put source audiobooks in:"
-msgstr "Umieść nowe audiobooki w:"
-
-#: templates/archive/list_published.html:6
-msgid "Published audiobooks"
-msgstr "Opublikowane audiobooki"
-
-#: templates/archive/list_publishing.html:6
-msgid "Audiobooks being published"
-msgstr "Aktualnie publikowane audiobooki"
-
-#: templates/archive/list_unmanaged.html:5
-msgid "Unmanaged archive"
-msgstr "Audiobooki archiwalne"
-
-#: templates/archive/list_unpublished.html:6
-msgid "Unpublished audiobooks"
-msgstr "Nie opublikowane audiobooki"
-
-#~ msgid "arranger"
-#~ msgstr "aranżer"
-
-#~ msgid "Audiobook marked for publishing with tags:"
-#~ msgstr "Audiobook zaznaczony do publikacji z tagami:"
-
-#~ msgid "Publishing already in progress."
-#~ msgstr "Publikowanie rozpoczÄ™te."
diff --git a/apps/archive/migrations/0001_initial.py b/apps/archive/migrations/0001_initial.py
deleted file mode 100644 (file)
index ac5a2c9..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-# encoding: utf-8
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-class Migration(SchemaMigration):
-
-    def forwards(self, orm):
-        
-        # Adding model 'Project'
-        db.create_table('archive_project', (
-            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=128, db_index=True)),
-            ('sponsors', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
-        ))
-        db.send_create_signal('archive', ['Project'])
-
-        # Adding model 'Audiobook'
-        db.create_table('archive_audiobook', (
-            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('source_file', self.gf('django.db.models.fields.files.FileField')(max_length=100)),
-            ('title', self.gf('django.db.models.fields.CharField')(max_length=255)),
-            ('artist', self.gf('django.db.models.fields.CharField')(max_length=255)),
-            ('conductor', self.gf('django.db.models.fields.CharField')(max_length=255)),
-            ('encoded_by', self.gf('django.db.models.fields.CharField')(max_length=255)),
-            ('date', self.gf('django.db.models.fields.CharField')(max_length=255)),
-            ('project', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['archive.Project'])),
-            ('url', self.gf('django.db.models.fields.URLField')(max_length=255)),
-            ('modified', self.gf('django.db.models.fields.DateTimeField')(null=True)),
-            ('published_tags', self.gf('jsonfield.fields.JSONField')(null=True)),
-            ('mp3_file', self.gf('django.db.models.fields.files.FileField')(max_length=100, null=True)),
-            ('ogg_file', self.gf('django.db.models.fields.files.FileField')(max_length=100, null=True)),
-            ('publishing_tags', self.gf('jsonfield.fields.JSONField')(null=True)),
-            ('publish_wait', self.gf('django.db.models.fields.DateTimeField')(null=True)),
-            ('publishing', self.gf('django.db.models.fields.BooleanField')(default=False)),
-            ('published', self.gf('django.db.models.fields.DateTimeField')(null=True)),
-        ))
-        db.send_create_signal('archive', ['Audiobook'])
-
-
-    def backwards(self, orm):
-        
-        # Deleting model 'Project'
-        db.delete_table('archive_project')
-
-        # Deleting model 'Audiobook'
-        db.delete_table('archive_audiobook')
-
-
-    models = {
-        'archive.audiobook': {
-            'Meta': {'ordering': "('title',)", 'object_name': 'Audiobook'},
-            'artist': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'conductor': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'encoded_by': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'modified': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'mp3_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True'}),
-            'ogg_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True'}),
-            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['archive.Project']"}),
-            'publish_wait': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'published': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'published_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
-            'publishing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'publishing_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
-            'source_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'url': ('django.db.models.fields.URLField', [], {'max_length': '255'})
-        },
-        'archive.project': {
-            'Meta': {'ordering': "('name',)", 'object_name': 'Project'},
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'}),
-            'sponsors': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        }
-    }
-
-    complete_apps = ['archive']
diff --git a/apps/archive/migrations/0002_auto__del_field_audiobook_publishing_tags__del_field_audiobook_publish.py b/apps/archive/migrations/0002_auto__del_field_audiobook_publishing_tags__del_field_audiobook_publish.py
deleted file mode 100644 (file)
index 03cb130..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-# encoding: utf-8
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-class Migration(SchemaMigration):
-
-    def forwards(self, orm):
-        
-        # Deleting field 'Audiobook.publishing_tags'
-        db.delete_column('archive_audiobook', 'publishing_tags')
-
-        # Deleting field 'Audiobook.published_tags'
-        db.delete_column('archive_audiobook', 'published_tags')
-
-        # Deleting field 'Audiobook.publish_wait'
-        db.delete_column('archive_audiobook', 'publish_wait')
-
-        # Deleting field 'Audiobook.published'
-        db.delete_column('archive_audiobook', 'published')
-
-        # Deleting field 'Audiobook.publishing'
-        db.delete_column('archive_audiobook', 'publishing')
-
-        # Adding field 'Audiobook.mp3_status'
-        db.add_column('archive_audiobook', 'mp3_status', self.gf('django.db.models.fields.SmallIntegerField')(null=True), keep_default=False)
-
-        # Adding field 'Audiobook.mp3_task'
-        db.add_column('archive_audiobook', 'mp3_task', self.gf('django.db.models.fields.CharField')(max_length=64, null=True), keep_default=False)
-
-        # Adding field 'Audiobook.mp3_tags'
-        db.add_column('archive_audiobook', 'mp3_tags', self.gf('jsonfield.fields.JSONField')(null=True), keep_default=False)
-
-        # Adding field 'Audiobook.mp3_published_tags'
-        db.add_column('archive_audiobook', 'mp3_published_tags', self.gf('jsonfield.fields.JSONField')(null=True), keep_default=False)
-
-        # Adding field 'Audiobook.mp3_published'
-        db.add_column('archive_audiobook', 'mp3_published', self.gf('django.db.models.fields.DateTimeField')(null=True), keep_default=False)
-
-        # Adding field 'Audiobook.ogg_status'
-        db.add_column('archive_audiobook', 'ogg_status', self.gf('django.db.models.fields.SmallIntegerField')(null=True), keep_default=False)
-
-        # Adding field 'Audiobook.ogg_task'
-        db.add_column('archive_audiobook', 'ogg_task', self.gf('django.db.models.fields.CharField')(max_length=64, null=True), keep_default=False)
-
-        # Adding field 'Audiobook.ogg_tags'
-        db.add_column('archive_audiobook', 'ogg_tags', self.gf('jsonfield.fields.JSONField')(null=True), keep_default=False)
-
-        # Adding field 'Audiobook.ogg_published_tags'
-        db.add_column('archive_audiobook', 'ogg_published_tags', self.gf('jsonfield.fields.JSONField')(null=True), keep_default=False)
-
-        # Adding field 'Audiobook.ogg_published'
-        db.add_column('archive_audiobook', 'ogg_published', self.gf('django.db.models.fields.DateTimeField')(null=True), keep_default=False)
-
-
-    def backwards(self, orm):
-        
-        # Adding field 'Audiobook.publishing_tags'
-        db.add_column('archive_audiobook', 'publishing_tags', self.gf('jsonfield.fields.JSONField')(null=True), keep_default=False)
-
-        # Adding field 'Audiobook.published_tags'
-        db.add_column('archive_audiobook', 'published_tags', self.gf('jsonfield.fields.JSONField')(null=True), keep_default=False)
-
-        # Adding field 'Audiobook.publish_wait'
-        db.add_column('archive_audiobook', 'publish_wait', self.gf('django.db.models.fields.DateTimeField')(null=True), keep_default=False)
-
-        # Adding field 'Audiobook.published'
-        db.add_column('archive_audiobook', 'published', self.gf('django.db.models.fields.DateTimeField')(null=True), keep_default=False)
-
-        # Adding field 'Audiobook.publishing'
-        db.add_column('archive_audiobook', 'publishing', self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=False)
-
-        # Deleting field 'Audiobook.mp3_status'
-        db.delete_column('archive_audiobook', 'mp3_status')
-
-        # Deleting field 'Audiobook.mp3_task'
-        db.delete_column('archive_audiobook', 'mp3_task')
-
-        # Deleting field 'Audiobook.mp3_tags'
-        db.delete_column('archive_audiobook', 'mp3_tags')
-
-        # Deleting field 'Audiobook.mp3_published_tags'
-        db.delete_column('archive_audiobook', 'mp3_published_tags')
-
-        # Deleting field 'Audiobook.mp3_published'
-        db.delete_column('archive_audiobook', 'mp3_published')
-
-        # Deleting field 'Audiobook.ogg_status'
-        db.delete_column('archive_audiobook', 'ogg_status')
-
-        # Deleting field 'Audiobook.ogg_task'
-        db.delete_column('archive_audiobook', 'ogg_task')
-
-        # Deleting field 'Audiobook.ogg_tags'
-        db.delete_column('archive_audiobook', 'ogg_tags')
-
-        # Deleting field 'Audiobook.ogg_published_tags'
-        db.delete_column('archive_audiobook', 'ogg_published_tags')
-
-        # Deleting field 'Audiobook.ogg_published'
-        db.delete_column('archive_audiobook', 'ogg_published')
-
-
-    models = {
-        'archive.audiobook': {
-            'Meta': {'ordering': "('title',)", 'object_name': 'Audiobook'},
-            'artist': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'conductor': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'encoded_by': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'modified': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'mp3_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True'}),
-            'mp3_published': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'mp3_published_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
-            'mp3_status': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True'}),
-            'mp3_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
-            'mp3_task': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}),
-            'ogg_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True'}),
-            'ogg_published': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'ogg_published_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
-            'ogg_status': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True'}),
-            'ogg_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
-            'ogg_task': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}),
-            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['archive.Project']"}),
-            'source_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'url': ('django.db.models.fields.URLField', [], {'max_length': '255'})
-        },
-        'archive.project': {
-            'Meta': {'ordering': "('name',)", 'object_name': 'Project'},
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'}),
-            'sponsors': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        }
-    }
-
-    complete_apps = ['archive']
diff --git a/apps/archive/migrations/0003_auto__add_field_audiobook_source_sha1__add_field_audiobook_translator.py b/apps/archive/migrations/0003_auto__add_field_audiobook_source_sha1__add_field_audiobook_translator.py
deleted file mode 100644 (file)
index 9711b72..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-# encoding: utf-8
-import datetime
-from hashlib import sha1
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-
-def sha1_file(f):
-    sha = sha1()
-    for piece in iter(lambda: f.read(1024*1024), ''):
-        sha.update(piece)
-    return sha.hexdigest()
-
-
-class Migration(SchemaMigration):
-
-    def forwards(self, orm):
-        
-        # Adding field 'Audiobook.source_sha1'
-        db.add_column('archive_audiobook', 'source_sha1', self.gf('django.db.models.fields.CharField')(default='', max_length=40), keep_default=False)
-
-        # Adding field 'Audiobook.translator'
-        db.add_column('archive_audiobook', 'translator', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True), keep_default=False)
-
-        if not db.dry_run:
-            for obj in orm.Audiobook.objects.all():
-                try:
-                    f = open(obj.source_file.path)
-                except ValueError, e:
-                    print "Audiobook has no source file"
-                    pass
-                else:
-                    obj.source_sha1 = sha1_file(f)
-                    f.close()
-                    obj.save()
-
-
-    def backwards(self, orm):
-        
-        # Deleting field 'Audiobook.source_sha1'
-        db.delete_column('archive_audiobook', 'source_sha1')
-
-        # Deleting field 'Audiobook.translator'
-        db.delete_column('archive_audiobook', 'translator')
-
-
-    models = {
-        'archive.audiobook': {
-            'Meta': {'ordering': "('title',)", 'object_name': 'Audiobook'},
-            'artist': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'conductor': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'encoded_by': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'modified': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'mp3_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True'}),
-            'mp3_published': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'mp3_published_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
-            'mp3_status': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True'}),
-            'mp3_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
-            'mp3_task': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}),
-            'ogg_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True'}),
-            'ogg_published': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'ogg_published_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
-            'ogg_status': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True'}),
-            'ogg_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
-            'ogg_task': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}),
-            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['archive.Project']"}),
-            'source_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
-            'source_sha1': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'translator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'url': ('django.db.models.fields.URLField', [], {'max_length': '255'})
-        },
-        'archive.project': {
-            'Meta': {'ordering': "('name',)", 'object_name': 'Project'},
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'}),
-            'sponsors': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        }
-    }
-
-    complete_apps = ['archive']
diff --git a/apps/archive/migrations/0004_auto__chg_field_audiobook_source_file.py b/apps/archive/migrations/0004_auto__chg_field_audiobook_source_file.py
deleted file mode 100644 (file)
index 95d630e..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-# encoding: utf-8
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-class Migration(SchemaMigration):
-
-    def forwards(self, orm):
-        
-        # Changing field 'Audiobook.source_file'
-        db.alter_column('archive_audiobook', 'source_file', self.gf('django.db.models.fields.files.FileField')(max_length=255))
-
-
-    def backwards(self, orm):
-        
-        # Changing field 'Audiobook.source_file'
-        db.alter_column('archive_audiobook', 'source_file', self.gf('django.db.models.fields.files.FileField')(max_length=100))
-
-
-    models = {
-        'archive.audiobook': {
-            'Meta': {'ordering': "('title',)", 'object_name': 'Audiobook'},
-            'artist': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'conductor': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'encoded_by': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'modified': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'mp3_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True'}),
-            'mp3_published': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'mp3_published_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
-            'mp3_status': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True'}),
-            'mp3_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
-            'mp3_task': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}),
-            'ogg_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True'}),
-            'ogg_published': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'ogg_published_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
-            'ogg_status': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True'}),
-            'ogg_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
-            'ogg_task': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}),
-            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['archive.Project']"}),
-            'source_file': ('django.db.models.fields.files.FileField', [], {'max_length': '255'}),
-            'source_sha1': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'translator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'url': ('django.db.models.fields.URLField', [], {'max_length': '255'})
-        },
-        'archive.project': {
-            'Meta': {'ordering': "('name',)", 'object_name': 'Project'},
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'}),
-            'sponsors': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        }
-    }
-
-    complete_apps = ['archive']
diff --git a/apps/archive/migrations/0005_auto__add_field_audiobook_part_name__add_field_audiobook_index__add_fi.py b/apps/archive/migrations/0005_auto__add_field_audiobook_part_name__add_field_audiobook_index__add_fi.py
deleted file mode 100644 (file)
index c163971..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-# -*- coding: utf-8 -*-
-from south.utils import datetime_utils as datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-
-class Migration(SchemaMigration):
-
-    def forwards(self, orm):
-        # Adding field 'Audiobook.part_name'
-        db.add_column(u'archive_audiobook', 'part_name',
-                      self.gf('django.db.models.fields.CharField')(default='', max_length=255, blank=True),
-                      keep_default=False)
-
-        # Adding field 'Audiobook.index'
-        db.add_column(u'archive_audiobook', 'index',
-                      self.gf('django.db.models.fields.IntegerField')(default=0),
-                      keep_default=False)
-
-        # Adding field 'Audiobook.parts_count'
-        db.add_column(u'archive_audiobook', 'parts_count',
-                      self.gf('django.db.models.fields.IntegerField')(default=1),
-                      keep_default=False)
-
-
-    def backwards(self, orm):
-        # Deleting field 'Audiobook.part_name'
-        db.delete_column(u'archive_audiobook', 'part_name')
-
-        # Deleting field 'Audiobook.index'
-        db.delete_column(u'archive_audiobook', 'index')
-
-        # Deleting field 'Audiobook.parts_count'
-        db.delete_column(u'archive_audiobook', 'parts_count')
-
-
-    models = {
-        u'archive.audiobook': {
-            'Meta': {'ordering': "('title',)", 'object_name': 'Audiobook'},
-            'artist': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'conductor': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'date': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'encoded_by': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'index': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
-            'modified': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'mp3_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True'}),
-            'mp3_published': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'mp3_published_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
-            'mp3_status': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True'}),
-            'mp3_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
-            'mp3_task': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}),
-            'ogg_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True'}),
-            'ogg_published': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'ogg_published_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
-            'ogg_status': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True'}),
-            'ogg_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
-            'ogg_task': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}),
-            'part_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'blank': 'True'}),
-            'parts_count': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
-            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['archive.Project']"}),
-            'source_file': ('django.db.models.fields.files.FileField', [], {'max_length': '255'}),
-            'source_sha1': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
-            'translator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
-            'url': ('django.db.models.fields.URLField', [], {'max_length': '255'})
-        },
-        u'archive.project': {
-            'Meta': {'ordering': "('name',)", 'object_name': 'Project'},
-            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'}),
-            'sponsors': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
-        }
-    }
-
-    complete_apps = ['archive']
\ No newline at end of file
diff --git a/apps/archive/migrations/__init__.py b/apps/archive/migrations/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/apps/archive/models.py b/apps/archive/models.py
deleted file mode 100644 (file)
index c75a874..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-# -*- coding: utf-8 -*-
-import os.path
-
-from django.db import models
-from time import sleep
-from jsonfield.fields import JSONField
-from django.utils.encoding import force_bytes
-from django.utils.translation import ugettext_lazy as _
-from archive.constants import status
-from archive.settings import FILES_SAVE_PATH, ADVERT, LICENSE, ORGANIZATION, PROJECT
-from archive.utils import OverwriteStorage, sha1_file
-
-# Create your models here.
-
-
-class Project(models.Model):
-    """ an audiobook project, needed for specyfing sponsors """
-
-    name = models.CharField(max_length=128, unique=True, db_index=True, verbose_name="Nazwa")
-    sponsors = models.TextField(blank=True, null=True, verbose_name="Sponsorzy")
-
-    class Meta:
-        verbose_name = _("project")
-        verbose_name_plural = _("projects")
-        ordering = ("name",)
-
-    def __unicode__(self):
-        return self.name
-
-
-def source_upload_to(intance, filename):
-    return os.path.join(FILES_SAVE_PATH, filename) # FIXME: what about really long file names?
-
-
-class Audiobook(models.Model):
-    source_file = models.FileField(upload_to=source_upload_to, max_length=255, 
-            verbose_name=_('source file'), editable=False)
-    source_sha1 = models.CharField(max_length=40, editable=False)
-
-    title = models.CharField(max_length=255, verbose_name=_('title'))
-    part_name = models.CharField(max_length=255, verbose_name=_('part name'), help_text=_('eg. chapter in a novel'),
-                                 default='', blank=True)
-    index = models.IntegerField(verbose_name=_('index'), default=0)
-    parts_count = models.IntegerField(verbose_name=_('parts count'), default=1)
-    artist = models.CharField(max_length=255, verbose_name=_('artist'))
-    conductor = models.CharField(max_length=255, verbose_name=_('conductor'))
-    encoded_by = models.CharField(max_length=255, verbose_name=_('encoded by'))
-    date = models.CharField(max_length=255, verbose_name=_('date'))
-    project = models.ForeignKey(Project, verbose_name=_('project'))
-    url = models.URLField(max_length=255, verbose_name=_('book url'))
-    translator = models.CharField(max_length=255, null=True, blank=True, verbose_name=_('translator'))
-    modified = models.DateTimeField(null=True, editable=False)
-
-    # publishing process
-    mp3_status = models.SmallIntegerField(null=True, editable=False, choices=status.choices)
-    mp3_task = models.CharField(max_length=64, null=True, editable=False)
-    mp3_tags = JSONField(null=True, editable=False)
-    mp3_file = models.FileField(null=True, upload_to='archive/final', storage=OverwriteStorage(), editable=False)
-    mp3_published_tags = JSONField(null=True, editable=False)
-    mp3_published = models.DateTimeField(null=True, editable=False)
-
-    ogg_status = models.SmallIntegerField(null=True, editable=False, choices=status.choices)
-    ogg_task = models.CharField(max_length=64, null=True, editable=False)
-    ogg_tags = JSONField(null=True, editable=False)
-    ogg_file = models.FileField(null=True, upload_to='archive/final', storage=OverwriteStorage(), editable=False)
-    ogg_published_tags = JSONField(null=True, editable=False)
-    ogg_published = models.DateTimeField(null=True, editable=False)
-
-
-    class Meta:
-        verbose_name = _("audiobook")
-        verbose_name_plural = _("audiobooks")
-        ordering = ("title",)
-
-    def __unicode__(self):
-        return self.title
-
-    def published(self):
-        return self.mp3_published and self.ogg_published
-
-    def get_source_sha1(self):
-        source_sha1 = self.source_sha1
-        if self.pk:
-            source_sha1 = type(self).objects.get(pk=self.pk).source_sha1
-            while source_sha1 == 'wait':
-                sleep(10)
-        if not source_sha1:
-            self.source_sha1 = 'wait'
-            if self.pk:
-                type(self).objects.filter(pk=self.pk).update(source_sha1='wait')
-            try:
-                f = open(force_bytes(self.source_file.path))
-                source_sha1 = sha1_file(f)
-                self.source_sha1 = source_sha1
-                if self.pk:
-                    type(self).objects.filter(pk=self.pk).update(source_sha1=source_sha1)
-            except:
-                self.source_sha1 = ''
-                if self.pk:
-                    type(self).objects.filter(pk=self.pk).update(source_sha1='')
-                return None
-        return source_sha1
-
-
-    def new_publish_tags(self):
-        title = self.title
-        if self.translator:
-            title += u' (tÅ‚um. %s)' % self.translator
-
-        copyright = u"%s %s. Licensed to the public under %s verify at %s" % (
-                self.date, ORGANIZATION, LICENSE, self.url)
-
-        comment = u"Audiobook nagrany w ramach projektu %s%s.\n%s" % (
-                    self.project.name,
-                    u" finansowanego przez %s" % self.project.sponsors if self.project.sponsors else "",
-                    ADVERT)
-
-        tags = {
-            'album': PROJECT,
-            'albumartist': ORGANIZATION,
-            'artist': self.artist,
-            'comment': comment,
-            'conductor': self.conductor,
-            'contact': self.url,
-            'copyright': copyright,
-            'date': self.date,
-            'genre': u'Speech',
-            'language': u'pol',
-            'license': LICENSE,
-            'organization': ORGANIZATION,
-            'title': title,
-            #'flac_sha1': self.get_source_sha1(),
-            'project': self.project.name,
-            'funded_by': self.project.sponsors,
-        }
-        if self.source_sha1 and self.source_sha1 != 'wait':
-            tags['flac_sha1'] = self.source_sha1
-        return tags
-
diff --git a/apps/archive/settings.py b/apps/archive/settings.py
deleted file mode 100644 (file)
index 476f6c7..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import os.path
-from django.conf import settings
-
-# this is where the end user puts new files
-try:
-    NEW_PATH = settings.ARCHIVE_NEW_PATH
-except AttributeError:
-    NEW_PATH = os.path.abspath(os.path.join(settings.MEDIA_ROOT,
-                        "archive/new"))
-
-# here the application keeps its managed files
-try:
-    FILES_PATH = settings.ARCHIVE_FILES_PATH
-except AttributeError:
-    FILES_PATH = os.path.abspath(os.path.join(settings.MEDIA_ROOT,
-                        "archive/files"))
-
-if FILES_PATH.startswith(settings.MEDIA_ROOT):
-    FILES_SAVE_PATH = FILES_PATH[len(settings.MEDIA_ROOT):].lstrip('/')
-
-
-# here the app keeps the unmanaged (archive) files
-try:
-    UNMANAGED_PATH = settings.ARCHIVE_UNMANAGED_PATH
-except AttributeError:
-    UNMANAGED_PATH = os.path.abspath(os.path.join(settings.MEDIA_ROOT,
-                        "archive/unmanaged"))
-
-
-# here the app keeps the resulting (published) files
-try:
-    FINAL_PATH = settings.ARCHIVE_FINAL_PATH
-except AttributeError:
-    FINAL_PATH = os.path.abspath(os.path.join(settings.MEDIA_ROOT,
-                        "archive/final"))
-
-
-# here the app keeps temporary build files
-try:
-    BUILD_PATH = settings.ARCHIVE_BUILD_PATH
-except AttributeError:
-    BUILD_PATH = os.path.abspath(os.path.join(settings.MEDIA_ROOT,
-                        "archive/build"))
-
-# upload conf
-try:
-    UPLOAD_HOST = settings.ARCHIVE_UPLOAD_HOST
-except AttributeError:
-    UPLOAD_HOST = 'wolnelektury.pl'
-
-try:
-    UPLOAD_USER = settings.ARCHIVE_UPLOAD_USER
-except AttributeError:
-    UPLOAD_USER = 'username'
-
-try:
-    UPLOAD_PASSWORD = settings.ARCHIVE_UPLOAD_PASSWORD
-except AttributeError:
-    UPLOAD_PASSWORD = None
-
-try:
-    UPLOAD_PATH = settings.ARCHIVE_UPLOAD_PATH
-except AttributeError:
-    UPLOAD_PATH = ''
-
-try:
-    UPLOAD_CMD = settings.ARCHIVE_UPLOAD_CMD
-except AttributeError:
-    UPLOAD_CMD = '/path/to/manage.py savemedia'
-
-try:
-    UPLOAD_SUDO = settings.ARCHIVE_UPLOAD_SUDO
-except AttributeError:
-    UPLOAD_SUDO = None
-
-
-try:
-    PROJECT = settings.ARCHIVE_PROJECT
-except AttributeError:
-    PROJECT = u'Wolne Lektury'
-
-try:
-    LICENSE = settings.ARCHIVE_LICENSE
-except AttributeError:
-    LICENSE = u'http://creativecommons.org/licenses/by-sa/3.0/deed.pl'
-
-try:
-    ORGANIZATION = settings.ARCHIVE_ORGANIZATION
-except AttributeError:
-    ORGANIZATION = u'Fundacja Nowoczesna Polska'
-
-try:
-    ADVERT = settings.ARCHIVE_ADVERT
-except AttributeError:
-    ADVERT = u"""
-Przekaż 1% podatku na rozwój Wolnych Lektur:
-Fundacja Nowoczesna Polska
-KRS 0000070056
-http://nowoczesnapolska.org.pl/wesprzyj_nas/
-"""
-
-try:
-    COVER_IMAGE = settings.ARCHIVE_COVER_IMAGE
-except AttributeError:
-    COVER_IMAGE = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'cover.png')
-
diff --git a/apps/archive/static/style.css b/apps/archive/static/style.css
deleted file mode 100755 (executable)
index 434b87b..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-body {
-    margin: 0;
-    font-family: verdana, sans-serif;
-    font-size: 12px;
-}
-
-a {
-    color: #bf6000;
-    text-decoration: none;
-}
-
-.clr {
-    clear: both;
-}
-
-
-#repo-zones-nav {
-    padding: 5px 5px 0 10px;
-    background: #ffdfbf;
-    border-bottom: 1px solid #ff8000;
-}
-
-#repo-zones-nav a, #repo-zones-nav span {
-    display: block;
-    float: left;
-    padding: 5px 20px 5px 20px;
-    margin-bottom: -1px;
-    border-width: 1px;
-    border-style: solid;
-    border-color: rgba(0,0,0,0);
-}
-
-#repo-zones-nav .active {
-    background: white;
-    border-color: #ff8000 #ff8000 white #ff8000;
-}
-
-#content {
-    padding: 10px;
-}
-
-
-.errorlist {
-    margin: 0;
-}
-
-.errorlist li {
-    color: red;
-}
-
-
-.tags, .multiple_tags {
-    border-collapse: collapse;
-}
-
-.tags td, .tags th, .multiple_tags td, .multiple_tags th {
-    border: 1px solid #ddd;
-    padding: 3px;
-}
-
-
-.list-published-tag, .list-publishing-tag {
-    font-size: .8em;
-}
-.list-publishing-tag {
-    color: #aaa;
-}
diff --git a/apps/archive/tasks.py b/apps/archive/tasks.py
deleted file mode 100644 (file)
index a46b79c..0000000
+++ /dev/null
@@ -1,214 +0,0 @@
-from datetime import datetime
-import errno
-import mimetypes
-import os
-import os.path
-import pipes
-import stat
-import subprocess
-from tempfile import NamedTemporaryFile
-from time import sleep
-
-#from celery.decorators import task
-from celery.task import Task
-from django.db.models import F
-from fabric import api
-from fabric.network import disconnect_all
-from mutagen import File
-from mutagen import id3
-
-import mutagen
-
-from archive.constants import status
-from archive.models import Audiobook
-from archive.settings import (BUILD_PATH, COVER_IMAGE,
-    UPLOAD_HOST, UPLOAD_USER, UPLOAD_PASSWORD, UPLOAD_PATH, UPLOAD_CMD, UPLOAD_SUDO)
-from archive.utils import ExistingFile
-
-api.env.host_string = UPLOAD_HOST
-api.env.user = UPLOAD_USER
-api.env.password = UPLOAD_PASSWORD
-
-class AudioFormatTask(Task):
-    abstract = True
-
-    class RemoteOperationError(BaseException):
-        pass
-
-    @classmethod
-    def set_status(cls, aid, status):
-        Audiobook.objects.filter(pk=aid).update(
-            **{'%s_status' % cls.ext: status})
-
-    @staticmethod
-    def encode(in_path, out_path):
-        raise NotImplemented
-
-    @classmethod
-    def set_tags(cls, audiobook, file_name):
-        tags = getattr(audiobook, "%s_tags" % cls.ext)['tags']
-        if not tags.get('flac_sha1'):
-            tags['flac_sha1'] = audiobook.get_source_sha1()
-        audio = File(file_name)
-        for k, v in tags.items():
-            audio[k] = v
-        audio.save()
-
-    @classmethod
-    def save(cls, audiobook, file_name):
-        field = "%s_file" % cls.ext
-        getattr(audiobook, field).save(
-            "%d.%s" % (audiobook.pk, cls.ext),
-            ExistingFile(file_name),
-            save=False
-            )
-        os.chmod(getattr(audiobook, field).path, stat.S_IREAD|stat.S_IWRITE|stat.S_IRGRP|stat.S_IROTH)
-        Audiobook.objects.filter(pk=audiobook.pk).update(
-            **{field: getattr(audiobook, field)})
-
-    @classmethod
-    def published(cls, aid):
-        kwargs = {
-            "%s_published_tags" % cls.ext: F("%s_tags" % cls.ext),
-            "%s_tags" % cls.ext: None,
-            "%s_published" % cls.ext: datetime.now(),
-            '%s_status' % cls.ext: None,
-        }
-        Audiobook.objects.filter(pk=aid).update(**kwargs)
-
-    @classmethod
-    def put(cls, audiobook, path):
-        tags = getattr(audiobook, "%s_tags" % cls.ext)
-        prefix, slug = tags['url'].rstrip('/').rsplit('/', 1)
-        name = tags['name']
-        command = UPLOAD_CMD + (u' %s %s %s %s %s %s > output.txt' % (
-            pipes.quote(os.path.join(UPLOAD_PATH, os.path.basename(path))),
-            pipes.quote(slug),
-            pipes.quote(name),
-            pipes.quote(audiobook.part_name),
-            audiobook.index,
-            audiobook.parts_count,
-            )).encode('utf-8')
-        try:
-            api.put(path, UPLOAD_PATH)
-            if UPLOAD_SUDO:
-                api.sudo(command, user=UPLOAD_SUDO, shell=False)
-            else:
-                api.run(command)
-            disconnect_all()
-        except SystemExit, e:
-            raise cls.RemoteOperationError
-
-    def run(self, aid, publish=True):
-        aid = int(aid)
-        audiobook = Audiobook.objects.get(id=aid)
-        self.set_status(aid, status.ENCODING)
-
-        try:
-            os.makedirs(BUILD_PATH)
-        except OSError as e:
-            if e.errno == errno.EEXIST:
-                pass
-            else:
-                raise
-
-        out_file = NamedTemporaryFile(delete=False, prefix='%d-' % aid, suffix='.%s' % self.ext, dir=BUILD_PATH)
-        out_file.close()
-        self.encode(audiobook.source_file.path, out_file.name)
-        self.set_status(aid, status.TAGGING)
-        self.set_tags(audiobook, out_file.name)
-        self.set_status(aid, status.SENDING)
-
-        if publish:
-            self.put(audiobook, out_file.name)
-            self.published(aid)
-        else:
-            self.set_status(aid, None)
-
-        self.save(audiobook, out_file.name)
-
-    def on_failure(self, exc, task_id, args, kwargs, einfo):
-        aid = (args[0], kwargs.get('aid'))[0]
-        self.set_status(aid, None)
-
-
-class Mp3Task(AudioFormatTask):
-    ext = 'mp3'
-
-    # these shouldn't be staticmethods
-    def id3_text(tag, text):
-        return tag(encoding=1, text=text)
-    def id3_url(tag, text):
-        return tag(url=text)
-    def id3_comment(tag, text, lang=u'pol'):
-        return tag(encoding=1, lang=lang, desc=u'', text=text)
-    def id3_priv(tag, text, what=u''):
-        return tag(owner='wolnelektury.pl?%s' % what, data=text.encode('utf-8'))
-
-    TAG_MAP = {
-        'album': (id3_text, id3.TALB),
-        'albumartist': (id3_text, id3.TPE2),
-        'artist': (id3_text, id3.TPE1),
-        'conductor': (id3_text, id3.TPE3),
-        'copyright': (id3_text, id3.TCOP),
-        'date': (id3_text, id3.TDRC),
-        'genre': (id3_text, id3.TCON),
-        'language': (id3_text, id3.TLAN),
-        'organization': (id3_text, id3.TPUB),
-        'title': (id3_text, id3.TIT2),
-        'comment': (id3_comment, id3.COMM, 'pol'),
-        'contact': (id3_url, id3.WOAF),
-        'license': (id3_url, id3.WCOP),
-        'flac_sha1': (id3_priv, id3.PRIV, 'flac_sha1'),
-        'project': (id3_priv, id3.PRIV, 'project'),
-        'funded_by': (id3_priv, id3.PRIV, 'funded_by'),
-    }
-
-    @staticmethod
-    def encode(in_path, out_path):
-        # 44.1kHz 64kbps mono MP3
-        subprocess.check_call(['ffmpeg', 
-            '-i', in_path.encode('utf-8'),
-            '-ar', '44100',
-            '-ab', '64k',
-            '-ac', '1',
-            '-y',
-            '-acodec', 'libmp3lame',
-            out_path.encode('utf-8')
-            ])
-
-    @classmethod
-    def set_tags(cls, audiobook, file_name):
-        mp3_tags = audiobook.mp3_tags['tags']
-        if not mp3_tags.get('flac_sha1'):
-            mp3_tags['flac_sha1'] = audiobook.get_source_sha1()
-        audio = id3.ID3(file_name)
-        for k, v in mp3_tags.items():
-            factory_tuple = cls.TAG_MAP[k]
-            factory, tagtype = factory_tuple[:2]
-            audio.add(factory(tagtype, v, *factory_tuple[2:]))
-
-        if COVER_IMAGE:
-            mime = mimetypes.guess_type(COVER_IMAGE)
-            f = open(COVER_IMAGE)
-            audio.add(id3.APIC(encoding=0, mime=mime, type=3, desc=u'', data=f.read()))
-            f.close()
-
-        audio.save()
-
-
-class OggTask(AudioFormatTask):
-    ext = 'ogg'
-
-    @staticmethod
-    def encode(in_path, out_path):
-        # 44.1kHz 64kbps mono Ogg Vorbis
-        subprocess.check_call(['ffmpeg', 
-            '-i', in_path.encode('utf-8'),
-            '-ar', '44100',
-            '-ab', '64k',
-            '-ac', '1',
-            '-y',
-            '-acodec', 'libvorbis',
-            out_path.encode('utf-8')
-            ])
diff --git a/apps/archive/templates/404.html b/apps/archive/templates/404.html
deleted file mode 100644 (file)
index 467dacd..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-{% extends "archive/base.html" %}
-{% block content %}
-<h1>Nie znaleziono strony.</h1>
-
-<p>Nie znaleziono Å¼Ä…danej strony.</p>
-{% endblock %}
diff --git a/apps/archive/templates/500.html b/apps/archive/templates/500.html
deleted file mode 100644 (file)
index b46280d..0000000
+++ /dev/null
@@ -1 +0,0 @@
-BÅ‚Ä…d serwera.
diff --git a/apps/archive/templates/archive/base.html b/apps/archive/templates/archive/base.html
deleted file mode 100644 (file)
index 8ec34e9..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-{% extends "base.html" %}
-{% load i18n %}
-
-{% block repo-zones-nav %}
-    <a {% if division = "new" %}class="active" {% endif %}href="{% url 'list_new' %}">{% trans "New" %}</a>
-    <a {% if division = "unpublished" %}class="active" {% endif %}href="{% url 'list_unpublished' %}">{% trans "Unpublished" %}</a>
-    <a {% if division = "publishing" %}class="active" {% endif %}href="{% url 'list_publishing' %}">{% trans "Publishing" %}</a>
-    <a {% if division = "published" %}class="active" {% endif %}href="{% url 'list_published' %}">{% trans "Published" %}</a>
-    <a {% if division = "unmanaged" %}class="active" {% endif %}href="{% url 'list_unmanaged' %}">{% trans "Archive" %}</a>
-    {% if user.is_staff %}
-        <a href='{% url "admin:archive_project_changelist" %}'>{% trans "Projects" %}</a>
-    {% endif %}
-    {% if user.is_authenticated %}
-        <a href="{% url 'logout' %}" style='float: right;'>{% trans "Logout" %}</a>
-    {% else %}
-        <a href="{% url 'login' %}" style='float: right;'>{% trans "Login" %}</a>
-    {% endif %}
-    {% if user.is_staff %}
-        <a href='{% url "admin:index" %}' style='float: right;'>{% trans "Administration" %}</a>
-    {% endif %}
-    {% if user.is_authenticated %}
-        <span style='float: right;'>{{ user }}</span>
-    {% endif %}
-    <div class='clr' ></div>
-{% endblock %}
diff --git a/apps/archive/templates/archive/file_managed.html b/apps/archive/templates/archive/file_managed.html
deleted file mode 100755 (executable)
index 8917243..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-{% extends "archive/base.html" %}
-{% load i18n %}
-{% load tags %}
-
-{% block content %}
-
-<p>Plik ÅºródÅ‚owy: <a href='{% url "download" audiobook.id %}'>{{ path }}</a>
-(sha1: <tt>{{ audiobook.source_sha1 }}</tt>).
-</p>
-
-<h2>{% trans "Publishing" %}</h2>
-
-{% if audiobook.mp3_status or audiobook.ogg_status %}
-
-<h2>{% trans "Publishing pending" %}</h2>
-
-<form method="post" action="{% url 'cancel_publishing' audiobook.id %}">
-    {% csrf_token %}
-    <input type="submit" value="{% trans "Cancel publishing" %}" />
-</form>
-
-
-{% if audiobook.mp3_status %}
-    <hr/>
-    <h2>MP3</h2>
-
-    {% tags_table audiobook.mp3_tags.tags %}
-
-    <p>Status: <b>{{ audiobook.get_mp3_status_display }}</b></p>
-{% endif %}
-
-{% if audiobook.ogg_status %}
-    <hr/>
-    <h2>Ogg Vorbis</h2>
-
-    {% tags_table audiobook.ogg_tags.tags %}
-
-    <p>Status: <b>{{ audiobook.get_ogg_status_display }}</b></p>
-{% endif %}
-
-
-
-
-{% else %}
-    <table class='tags'>
-        {% tags_table audiobook.new_publish_tags 0 %}
-        <tr><th></th><td>
-
-            <form method="post" action="{% url 'publish' audiobook.id %}">
-                {% csrf_token %}
-                <input type="submit" value="{% trans "Publish" %}" />
-            </form>
-
-            {% if not audiobook.mp3_published or not audiobook.ogg_published %}
-            <form method="post" action="{% url 'convert' audiobook.id %}">
-                {% csrf_token %}
-                <input type="submit" value="{% trans "Convert without publishing" %}" />
-            </form>
-            {% endif %}
-
-        </td></tr>
-    </table>
-{% endif %}
-
-<hr/>
-{% if audiobook.mp3_file %}
-    <h2>{% trans "MP3 file" %}</h2>
-    <p><a href="{% url 'download' audiobook.id 'mp3' %}">{% trans "Download MP3 file." %}</a></p>
-    {% if audiobook.mp3_published %}
-        <p>{% trans "Published:" %} {{ audiobook.mp3_published }}</a></p>
-        {% if audiobook.mp3_published_tags.tags %}
-            {% tags_table audiobook.mp3_published_tags.tags %}
-        {% endif %}
-    {% else %}
-        <p>{% trans "Not published yet." %}</p>
-    {% endif %}
-{% else %}
-    <p>{% trans "MP3 file hasn't been generated yet." %}</p>
-{% endif %}
-
-<hr/>
-{% if audiobook.ogg_file %}
-    <h2>{% trans "Ogg Vorbis file" %}</h2>
-    <p><a href="{% url 'download' audiobook.id 'ogg' %}">{% trans "Download Ogg Vorbis file." %}</a></p>
-    {% if audiobook.ogg_published %}
-        <p>{% trans "Published:" %} {{ audiobook.ogg_published }}</a></p>
-        {% if audiobook.ogg_published_tags.tags %}
-            {% tags_table audiobook.ogg_published_tags.tags %}
-        {% endif %}
-    {% else %}
-        <p>{% trans "Not published yet." %}</p>
-    {% endif %}
-{% else %}
-    <p>{% trans "Ogg Vorbis file hasn't been generated yet." %}</p>
-{% endif %}
-
-
-
-
-<hr />
-
-
-
-
-<h2>{% trans "Update tags" %}</h2>
-
-Last modified: {{ audiobook.modified }}
-
-{% multiple_tags_table tags %}
-
-
-
-<form method='post' action='.'>
-    {% csrf_token %}
-    <table>
-        {{ form.as_table }}
-        <td></td><td><input type="submit" value='{% trans "Commit" %}' /></td></td>
-    </table>
-</form>
-
-
-
-<hr />
-
-
-
-<form method="post" action="{% url 'remove_to_archive' audiobook.id %}"
-    onsubmit='return confirm("{% trans "Are you sure you want to move this audiobook to archive?" %}")'>
-    {% csrf_token %}
-    <input type="submit" value="{% trans "Remove to archive" %}" />
-</form>
-
-
-
-{% endblock %}
diff --git a/apps/archive/templates/archive/file_new.html b/apps/archive/templates/archive/file_new.html
deleted file mode 100644 (file)
index acc161d..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-{% extends "archive/base.html" %}
-{% load i18n tags %}
-
-{% block content %}
-
-<form method="post" action="{% url 'move_to_archive' filename %}">
-    {% csrf_token %}
-    <input type="submit" value="{% trans "Move to archive" %}" />
-</form>
-
-
-{% multiple_tags_table tags %}
-
-
-<form method='post' action='.'>
-    {% csrf_token %}
-    <table>
-        {{ form.as_table }}
-        <td></td><td><input type="submit" value='{% trans "Commit" %}' /></td></td>
-    </table>
-</form>
-
-{% endblock %}
diff --git a/apps/archive/templates/archive/file_unmanaged.html b/apps/archive/templates/archive/file_unmanaged.html
deleted file mode 100755 (executable)
index 89f79ba..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-{% extends "archive/base.html" %}
-{% load i18n %}
-
-{% block messages %}
-    {% if err_exists %}
-        <p>{% trans "File with same name already exists!" %}</p>
-    {% endif %}
-{% endblock %}
-
-
-{% block content %}
-
-
-<ul id="tags">
-    {% for t, v in tags.items %}
-        <li>{{t}}
-        <ul>
-        {% for x in v %}
-            <li>{{x}}</li>
-        {% endfor %}
-        </ul></li>
-    {% endfor %}
-</ul>
-
-
-<form method="post" action="{% url 'move_to_new' filename %}">
-    {% csrf_token %}
-    <input type="submit" value="{% trans "Move to new files" %}" />
-</form>
-
-{% endblock %}
diff --git a/apps/archive/templates/archive/list.html b/apps/archive/templates/archive/list.html
deleted file mode 100644 (file)
index bcd85de..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-{% extends "archive/base.html" %}
-{% load i18n %}
-
-
-{% block content %}
-
-    <div id="file-list">
-        <h1>{% block file-list-title %}{% endblock %}</h1>
-        <div>{% block file-list-info %}{% endblock %}</div>
-        {% block file-list-wrapper %}
-            <ul>{% block file-list %}{% endblock %}</ul>
-        {% endblock file-list-wrapper %}
-    </div>
-
-{% endblock %}
diff --git a/apps/archive/templates/archive/list_new.html b/apps/archive/templates/archive/list_new.html
deleted file mode 100644 (file)
index fabf446..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-{% extends "archive/list.html" %}
-{% load i18n %}
-
-
-{% block file-list-title %}
-    {% trans "New audiobooks" %}
-{% endblock %}
-
-
-{% block file-list-info %}
-    {% trans "Put source audiobooks in:" %}
-    <span>{{ path }}</span>
-{% endblock %}
-
-
-{% block file-list %}
-    {% for file in objects %}
-        <li>
-            <a href='{% url "file_new" file|urlencode %}'>{{ file }}</a>
-        </li>
-    {% endfor %}
-{% endblock %}
diff --git a/apps/archive/templates/archive/list_published.html b/apps/archive/templates/archive/list_published.html
deleted file mode 100755 (executable)
index 5e61ba1..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-{% extends "archive/list.html" %}
-{% load i18n %}
-
-
-{% block file-list-title %}
-    {% trans "Published audiobooks" %}
-{% endblock %}
-
-
-{% block file-list-info %}
-{% endblock %}
-
-
-{% block file-list %}
-    {% for file in objects %}
-        <li>
-            <a href='{% url "file" file.id %}'>{{ file }}</a>
-        </li>
-    {% endfor %}
-{% endblock %}
diff --git a/apps/archive/templates/archive/list_publishing.html b/apps/archive/templates/archive/list_publishing.html
deleted file mode 100755 (executable)
index 6bcf9e0..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-{% extends "archive/list.html" %}
-{% load i18n %}
-
-
-{% block file-list-title %}
-    {% trans "Audiobooks being published" %}
-{% endblock %}
-
-
-
-{% block file-list-wrapper %}
-    {% for k, objects in status_objects %}
-        <h2>{{ k.1 }}</h2>
-        <ul>
-            {% for file in objects %}
-            <li>
-                <a href='{% url "file" file.id %}'>{{ file }}</a>
-                ({% if file.mp3_status = k.0 %}MP3{% if file.ogg_status = k.0 %}, {% endif %}{% endif %}{% if file.ogg_status = k.0 %}Ogg{% endif %})
-            </li>
-            {% endfor %}
-        </ul>
-    {% endfor %}
-{% endblock %}
diff --git a/apps/archive/templates/archive/list_unmanaged.html b/apps/archive/templates/archive/list_unmanaged.html
deleted file mode 100755 (executable)
index fd48c33..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-{% extends "archive/list.html" %}
-{% load i18n %}
-
-{% block file-list-title %}
-    {% trans "Unmanaged archive" %}
-{% endblock %}
-
-
-{% block file-list-info %}
-{% endblock %}
-
-
-{% block file-list %}
-    {% for file in objects %}
-        <li>
-            <a href='{% url "file_unmanaged" file|urlencode %}'>{{ file }}</a>
-        </li>
-    {% endfor %}
-{% endblock %}
diff --git a/apps/archive/templates/archive/list_unpublished.html b/apps/archive/templates/archive/list_unpublished.html
deleted file mode 100755 (executable)
index b901ef0..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-{% extends "archive/list.html" %}
-{% load i18n %}
-
-
-{% block file-list-title %}
-    {% trans "Unpublished audiobooks" %}
-{% endblock %}
-
-
-{% block file-list-info %}
-{% endblock %}
-
-
-{% block file-list %}
-    {% for file in objects %}
-        <li>
-            <a href='{% url "file" file.id %}'>{{ file }}</a>
-
-            {% if file.mp3_published %}
-                <span class="list-published-tag" title="{{ file.mp3_published }}">MP3</span>
-            {% else %}{% if file.mp3_status %}
-                <span class="list-publishing-tag" title="{{ file.get_mp3_status_display }}">MP3</span>
-            {% endif %}{% endif %}
-
-            {% if file.ogg_published %}
-                <span class="list-published-tag" title="{{ file.ogg_published }}">Ogg</span>
-            {% else %}{% if file.ogg_status %}
-                <span class="list-publishing-tag" title="{{ file.get_ogg_status_display }}">Ogg</span>
-            {% endif %}{% endif %}
-        </li>
-    {% endfor %}
-{% endblock %}
diff --git a/apps/archive/templates/archive/tags/multiple_tags_table.html b/apps/archive/templates/archive/tags/multiple_tags_table.html
deleted file mode 100755 (executable)
index 4609278..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-{% if table %}<table class='multiple_tags'>{% endif %}
-    {% for t, v in tags.items %}
-        <tr><th>{{ t }}</th><td>
-        {% for x in v %}
-            <div>{{ x|linebreaksbr }}</div>
-        {% endfor %}
-        </td></tr>
-    {% endfor %}
-{% if table %}</table>{% endif %}
diff --git a/apps/archive/templates/archive/tags/tags_table.html b/apps/archive/templates/archive/tags/tags_table.html
deleted file mode 100755 (executable)
index 70b9d48..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-{% if table %}<table class='tags'>{% endif %}
-    {% for t, v in tags.items %}
-        <tr><th>{{ t }}</th><td>
-            {{ v|linebreaksbr }}
-        </td></tr>
-    {% endfor %}
-{% if table %}</table>{% endif %}
diff --git a/apps/archive/templates/base.html b/apps/archive/templates/base.html
deleted file mode 100755 (executable)
index 5e0053f..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-{% load i18n %}
-<!DOCTYPE html>
-<html>
-<head>
-    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
-    <link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}style.css?20110808" />
-    <title>{% trans "Audiobook repository" %}</title>
-</head>
-<body>
-
-<div id="repo-zones-nav">
-    {% block repo-zones-nav %}&nbsp;{% endblock %}
-</div>
-
-<div id="messages">
-{% block messages %}{% endblock %}
-</div>
-
-<div id="content">
-{% block content %}{% endblock %}
-</div>
-
-</body>
-</html>
diff --git a/apps/archive/templates/registration/login.html b/apps/archive/templates/registration/login.html
deleted file mode 100755 (executable)
index 115cdf1..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-{% extends "base.html" %}
-{% load i18n %}
-
-{% block content %}
-
-<form method='post'>
-{% csrf_token %}
-{{ form.as_p }}
-<p><input type='submit' value='{% trans "Login" %}' /></p>
-</form>
-
-{% endblock %}
diff --git a/apps/archive/templatetags/__init__.py b/apps/archive/templatetags/__init__.py
deleted file mode 100755 (executable)
index e69de29..0000000
diff --git a/apps/archive/templatetags/tags.py b/apps/archive/templatetags/tags.py
deleted file mode 100755 (executable)
index 8e208a6..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-from django import template
-
-register = template.Library()
-
-@register.inclusion_tag('archive/tags/multiple_tags_table.html')
-def multiple_tags_table(tags, table=True):
-    new_tags = {}
-    if tags:
-        for k, v in tags.items():
-            if isinstance(v, list):
-                new_tags[k] = v
-            else:
-                new_tags[k] = [v]
-    return {"tags": new_tags, "table": table}
-
-
-@register.inclusion_tag('archive/tags/tags_table.html')
-def tags_table(tags, table=True):
-    if tags is None:
-        tags = {}
-    return locals()
diff --git a/apps/archive/tests.py b/apps/archive/tests.py
deleted file mode 100644 (file)
index 501deb7..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-"""
-This file demonstrates writing tests using the unittest module. These will pass
-when you run "manage.py test".
-
-Replace this with more appropriate tests for your application.
-"""
-
-from django.test import TestCase
-
-
-class SimpleTest(TestCase):
-    def test_basic_addition(self):
-        """
-        Tests that 1 + 1 always equals 2.
-        """
-        self.assertEqual(1 + 1, 2)
diff --git a/apps/archive/urls.py b/apps/archive/urls.py
deleted file mode 100644 (file)
index efaeeea..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-from django.conf.urls import patterns, include, url
-from django.views.generic import RedirectView
-
-urlpatterns = patterns('',
-    url(r'^$', RedirectView.as_view(url='new/')),
-
-    url(r'^new/$', 'archive.views.list_new', name="list_new"),
-    url(r'^new/(.+)/$', 'archive.views.file_new', name="file_new"),
-    url(r'^move_to_archive/(.+)/$', 'archive.views.move_to_archive', name="move_to_archive"),
-
-    url(r'^unpublished/$', 'archive.views.list_unpublished', name="list_unpublished"),
-    url(r'^publishing/$', 'archive.views.list_publishing', name="list_publishing"),
-    url(r'^published/$', 'archive.views.list_published', name="list_published"),
-    url(r'^file/(\d+)/$', 'archive.views.file_managed', name="file"),
-    url(r'^publish/(\d+)/$', 'archive.views.publish', name="publish"),
-    url(r'^convert/(\d+)/$', 'archive.views.publish', {'publish': False}, name="convert"),
-    url(r'^download/(\d+)/$', 'archive.views.download', name="download"),
-    url(r'^download/(\d+)\.(mp3|ogg)$', 'archive.views.download', name="download"),
-    url(r'^cancel/(\d+)/$', 'archive.views.cancel_publishing', name="cancel_publishing"),
-    url(r'^remove_to_archive/(\d+)/$', 'archive.views.remove_to_archive', name="remove_to_archive"),
-
-    url(r'^unmanaged/$', 'archive.views.list_unmanaged', name="list_unmanaged"),
-    url(r'^unmanaged/(.+)/$', 'archive.views.file_unmanaged', name="file_unmanaged"),
-    url(r'^move_to_new/(.+)/$', 'archive.views.move_to_new', name="move_to_new"),
-)
diff --git a/apps/archive/utils.py b/apps/archive/utils.py
deleted file mode 100644 (file)
index 3e89a8b..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-from hashlib import sha1
-import os
-import os.path
-from django.core.files.storage import FileSystemStorage
-from django.core.files.uploadedfile import UploadedFile
-
-
-class ExistingFile(UploadedFile):
-
-    def __init__(self, path, *args, **kwargs):
-        self.path = path
-        return super(ExistingFile, self).__init__(*args, **kwargs)
-
-    def temporary_file_path(self):
-        return self.path
-
-    def close(self):
-        pass
-
-
-class OverwriteStorage(FileSystemStorage):
-
-    def _save(self, name, content):
-        if self.exists(name):
-            self.delete(name)
-        return super(OverwriteStorage, self)._save(name, content)
-
-    def get_available_name(self, name):
-        return name
-
-
-def sha1_file(f):
-    sha = sha1()
-    for piece in iter(lambda: f.read(1024*1024), ''):
-        sha.update(piece)
-    return sha.hexdigest()
-
-
-def all_files(root_path):
-    root_len = len(root_path)
-    for path, dirs, files in os.walk(root_path):
-       for fname in files:
-           yield os.path.join(path, fname)[root_len:].lstrip('/')
-
diff --git a/apps/archive/views.py b/apps/archive/views.py
deleted file mode 100644 (file)
index b5850a0..0000000
+++ /dev/null
@@ -1,279 +0,0 @@
-# Create your views here.
-
-from datetime import datetime
-import os
-import os.path
-from urllib import quote
-
-from archive import settings
-from django.contrib.auth import logout
-from django.contrib.auth.decorators import permission_required
-from django.core.urlresolvers import reverse
-from django.db.models import Q, Max
-from django.http import Http404, HttpResponse
-from django.shortcuts import render, redirect, get_object_or_404
-from django.views.decorators.http import require_POST
-
-import mutagen
-
-from archive.constants import status
-from archive import models
-from archive.forms import AudiobookForm
-from archive import tasks
-from archive.utils import all_files
-
-
-def list_new(request):
-    division = 'new'
-
-    path = settings.NEW_PATH
-    objects = sorted(all_files(path))
-    return render(request, "archive/list_new.html", locals())
-
-
-@permission_required('archive.change_audiobook')
-def file_new(request, filename):
-    division = 'new'
-
-    filepath = filename.encode('utf-8')
-    root_filepath = os.path.join(settings.NEW_PATH, filename.encode('utf-8'))
-    if request.POST:
-        form = AudiobookForm(request.POST)
-        if form.is_valid():
-            try:
-                form.save(path=filepath)
-            except IOError:
-                raise Http404
-            return redirect(list_new)
-
-    try:
-        tags = mutagen.File(root_filepath)
-    except IOError:
-        raise Http404
-    d = {}
-    if tags:
-        for tag in tags:
-            value = tags[tag]
-            if isinstance(value, list):
-                d[tag] = value[0]
-            else:
-                d[tag] = value
-            if tag == 'project':
-                try:
-                    d[tag] = models.Project.objects.get(name=d[tag]).pk
-                except models.Project.DoesNotExist:
-                    d[tag] = None
-
-    if not request.POST:
-        form = AudiobookForm(d)
-    return render(request, "archive/file_new.html", locals())
-
-
-@require_POST
-@permission_required('archive.change_audiobook')
-def move_to_archive(request, filename):
-    """ move a new file to the unmanaged files dir """
-
-    filename_str = filename.encode('utf-8')
-    old_path = os.path.join(settings.NEW_PATH, filename_str)
-    new_path = os.path.join(settings.UNMANAGED_PATH, filename_str)
-    new_dir = os.path.split(new_path)[0]
-    if not os.path.isdir(new_dir):
-        os.makedirs(new_dir)
-
-    if not os.path.isfile(old_path):
-        raise Http404
-
-    try:
-        os.link(old_path, new_path)
-        os.unlink(old_path)
-    except OSError:
-        # destination file exists, don't overwrite it
-        # TODO: this should probably be more informative
-        return redirect(file_new, filename)
-
-    return redirect(list_new)
-
-
-@require_POST
-@permission_required('archive.change_audiobook')
-def remove_to_archive(request, aid):
-    """ move a managed file to the unmanaged files dir """
-
-    audiobook = get_object_or_404(models.Audiobook, id=aid)
-    old_path = audiobook.source_file.path
-    new_path = os.path.join(settings.UNMANAGED_PATH,
-        str(audiobook.source_file)[len(settings.FILES_SAVE_PATH):].lstrip('/'))
-    new_dir = os.path.split(new_path)[0]
-    if not os.path.isdir(new_dir):
-        os.makedirs(new_dir)
-
-    if not os.path.isfile(old_path):
-        raise Http404
-
-    success = False
-    try_new_path = new_path
-    try_number = 0
-    while not success:
-        try:
-            os.link(old_path, try_new_path)
-        except OSError:
-            # destination file exists, don't overwrite it
-            try_number += 1
-            parts = new_path.rsplit('.', 1)
-            parts[0] += '_%d' % try_number
-            try_new_path = ".".join(parts)
-        else:
-            os.unlink(old_path)
-            audiobook.delete()
-            success = True
-
-    return redirect(list_unmanaged)
-
-@require_POST
-@permission_required('archive.change_audiobook')
-def move_to_new(request, filename):
-    """ move a unmanaged file to new files dir """
-
-    filename_str = filename.encode('utf-8')
-    old_path = os.path.join(settings.UNMANAGED_PATH, filename_str)
-    new_path = os.path.join(settings.NEW_PATH, filename_str)
-    new_dir = os.path.split(new_path)[0]
-    if not os.path.isdir(new_dir):
-        os.makedirs(new_dir)
-
-    if not os.path.isfile(old_path):
-        raise Http404
-
-    try:
-        os.link(old_path, new_path)
-        os.unlink(old_path)
-    except OSError:
-        # destination file exists, don't overwrite it
-        # TODO: this should probably be more informative
-        return redirect(reverse(file_unmanaged, args=[filename]) + "?exists=1")
-
-    return redirect(list_unmanaged)
-
-
-@require_POST
-@permission_required('archive.change_audiobook')
-def publish(request, aid, publish=True):
-    """ mark file for publishing """
-    audiobook = get_object_or_404(models.Audiobook, id=aid)
-    tags = {
-        'name': audiobook.title,
-        'url': audiobook.url,
-        'tags': audiobook.new_publish_tags(),
-        }
-    audiobook.mp3_tags = tags
-    audiobook.ogg_tags = tags
-    audiobook.mp3_status = audiobook.ogg_status = status.WAITING
-    audiobook.save()
-    # isn't there a race here?
-    audiobook.mp3_task = tasks.Mp3Task.delay(aid, publish).task_id
-    audiobook.ogg_task = tasks.OggTask.delay(aid, publish).task_id
-    audiobook.save()
-
-    return redirect(file_managed, aid)
-
-
-@require_POST
-@permission_required('archive.change_audiobook')
-def cancel_publishing(request, aid):
-    """ cancel scheduled publishing """
-    audiobook = get_object_or_404(models.Audiobook, id=aid)
-    # TODO: cancel tasks
-    audiobook.mp3_status = None
-    audiobook.ogg_status = None
-    audiobook.save()
-    return redirect(file_managed, aid)
-
-
-def download(request, aid, which="source"):
-    if which not in ("source", "mp3", "ogg"):
-        raise Http404
-    audiobook = get_object_or_404(models.Audiobook, id=aid)
-    file_ = getattr(audiobook, "%s_file" % which)
-    if not file_:
-        raise Http404
-    ext = file_.path.rsplit('.', 1)[-1]
-    response = HttpResponse(mimetype='application/force-download')
-    
-    response['Content-Disposition'] = "attachment; filename*=UTF-8''%s.%s" % (
-        quote(audiobook.title.encode('utf-8'), safe=''), ext)
-    response['X-Sendfile'] = file_.path.encode('utf-8')
-    return response
-
-
-def list_unpublished(request):
-    division = 'unpublished'
-
-    objects = models.Audiobook.objects.filter(Q(mp3_published=None) | Q(ogg_published=None))
-    return render(request, "archive/list_unpublished.html", locals())
-
-
-def list_publishing(request):
-    division = 'publishing'
-
-    objects = models.Audiobook.objects.exclude(mp3_status=None, ogg_status=None)
-    objects_by_status = {}
-    for o in objects:
-        if o.mp3_status:
-            k = o.mp3_status, o.get_mp3_status_display()
-            objects_by_status.setdefault(k, []).append(o)
-        if o.ogg_status and o.ogg_status != o.mp3_status:
-            k = o.ogg_status, o.get_ogg_status_display()
-            objects_by_status.setdefault(k, []).append(o)
-    status_objects = sorted(objects_by_status.items(), reverse=True)
-
-    return render(request, "archive/list_publishing.html", locals())
-
-
-def list_published(request):
-    division = 'published'
-
-    objects = models.Audiobook.objects.exclude(Q(mp3_published=None) | Q(ogg_published=None))
-    return render(request, "archive/list_published.html", locals())
-
-
-@permission_required('archive.change_audiobook')
-def file_managed(request, id):
-    audiobook = get_object_or_404(models.Audiobook, id=id)
-
-    if request.POST:
-        form = AudiobookForm(request.POST, instance=audiobook)
-        if form.is_valid():
-            try:
-                form.save()
-            except IOError:
-                raise Http404
-
-    division = 'published' if audiobook.published() else 'unpublished'
-    path = audiobook.source_file.path[len(settings.FILES_PATH):].lstrip('/')
-
-    # for tags update
-    tags = mutagen.File(audiobook.source_file.path.encode('utf-8'))
-    if not tags:
-        tags = {}
-    form = AudiobookForm(instance=audiobook)
-
-    return render(request, "archive/file_managed.html", locals())
-
-
-def list_unmanaged(request):
-    division = 'unmanaged'
-
-    objects = sorted(all_files(settings.UNMANAGED_PATH))
-    return render(request, "archive/list_unmanaged.html", locals())
-
-
-def file_unmanaged(request, filename):
-    division = 'unmanaged'
-
-    tags = mutagen.File(os.path.join(settings.UNMANAGED_PATH, filename.encode('utf-8')))
-    if not tags:
-        tags = {}
-    
-    err_exists = request.GET.get('exists')
-    return render(request, "archive/file_unmanaged.html", locals())
diff --git a/audiobooks/__init__.py b/audiobooks/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/audiobooks/manage.py b/audiobooks/manage.py
deleted file mode 100755 (executable)
index 3e4eedc..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env python
-from django.core.management import execute_manager
-import imp
-try:
-    imp.find_module('settings') # Assumed to be in the same directory.
-except ImportError:
-    import sys
-    sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n" % __file__)
-    sys.exit(1)
-
-import settings
-
-if __name__ == "__main__":
-    execute_manager(settings)
diff --git a/audiobooks/settings.py b/audiobooks/settings.py
deleted file mode 100644 (file)
index 3f92da0..0000000
+++ /dev/null
@@ -1,198 +0,0 @@
-# Django settings for audiobooks project.
-# -*- coding: utf-8 -*-
-
-import os
-import sys
-PROJECT_ROOT = os.path.abspath(os.path.dirname(__file__))
-sys.path.insert(0, os.path.join(PROJECT_ROOT, "../apps"))
-
-
-
-DEBUG = False
-TEMPLATE_DEBUG = DEBUG
-
-ADMINS = (
-    # ('Your Name', 'your_email@example.com'),
-)
-
-MANAGERS = ADMINS
-
-DATABASES = {
-    'default': {
-        'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
-        'NAME': os.path.join(PROJECT_ROOT, 'dev.sqlite'),                      # Or path to database file if using sqlite3.
-        'USER': '',                      # Not used with sqlite3.
-        'PASSWORD': '',                  # Not used with sqlite3.
-        'HOST': '',                      # Set to empty string for localhost. Not used with sqlite3.
-        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
-    }
-}
-
-# Local time zone for this installation. Choices can be found here:
-# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
-# although not all choices may be available on all operating systems.
-# On Unix systems, a value of None will cause Django to use the same
-# timezone as the operating system.
-# If running in a Windows environment this must be set to the same as your
-# system time zone.
-TIME_ZONE = 'Europe/Warsaw'
-
-# Language code for this installation. All choices can be found here:
-# http://www.i18nguy.com/unicode/language-identifiers.html
-LANGUAGE_CODE = 'pl'
-
-SITE_ID = 1
-
-# If you set this to False, Django will make some optimizations so as not
-# to load the internationalization machinery.
-USE_I18N = True
-
-# If you set this to False, Django will not format dates, numbers and
-# calendars according to the current locale
-USE_L10N = True
-
-# Absolute filesystem path to the directory that will hold user-uploaded files.
-# Example: "/home/media/media.lawrence.com/media/"
-MEDIA_ROOT = os.path.join(PROJECT_ROOT, '../media')
-
-# URL that handles the media served from MEDIA_ROOT. Make sure to use a
-# trailing slash.
-# Examples: "http://media.lawrence.com/media/", "http://example.com/media/"
-MEDIA_URL = '/media/'
-
-# Absolute path to the directory static files should be collected to.
-# Don't put anything in this directory yourself; store your static files
-# in apps' "static/" subdirectories and in STATICFILES_DIRS.
-# Example: "/home/media/media.lawrence.com/static/"
-STATIC_ROOT = ''
-
-# URL prefix for static files.
-# Example: "http://media.lawrence.com/static/"
-STATIC_URL = '/static/'
-
-# URL prefix for admin static files -- CSS, JavaScript and images.
-# Make sure to use a trailing slash.
-# Examples: "http://foo.com/static/admin/", "/static/admin/".
-ADMIN_MEDIA_PREFIX = '/static/admin/'
-
-# Additional locations of static files
-STATICFILES_DIRS = (
-    # Put strings here, like "/home/html/static" or "C:/www/django/static".
-    # Always use forward slashes, even on Windows.
-    # Don't forget to use absolute paths, not relative paths.
-)
-
-# List of finder classes that know how to find static files in
-# various locations.
-STATICFILES_FINDERS = (
-    'django.contrib.staticfiles.finders.FileSystemFinder',
-    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
-#    'django.contrib.staticfiles.finders.DefaultStorageFinder',
-)
-
-# Make this unique, and don't share it with anybody.
-SECRET_KEY = '8ehk^-+pr(o)k6lh_gl9+ks6gkd9u#fka7fv%ikpk(c%llqa6%'
-
-# List of callables that know how to import templates from various sources.
-TEMPLATE_LOADERS = (
-    'django.template.loaders.filesystem.Loader',
-    'django.template.loaders.app_directories.Loader',
-#     'django.template.loaders.eggs.Loader',
-)
-
-MIDDLEWARE_CLASSES = (
-    'django.middleware.common.CommonMiddleware',
-    'django.contrib.sessions.middleware.SessionMiddleware',
-    'django.middleware.csrf.CsrfViewMiddleware',
-    'django.contrib.auth.middleware.AuthenticationMiddleware',
-    'django_cas.middleware.CASMiddleware',
-    'django.contrib.messages.middleware.MessageMiddleware',
-)
-
-AUTHENTICATION_BACKENDS = (
-    'django.contrib.auth.backends.ModelBackend',
-    'django_cas.backends.CASBackend',
-)
-
-ROOT_URLCONF = 'audiobooks.urls'
-
-TEMPLATE_DIRS = (
-    # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
-    # Always use forward slashes, even on Windows.
-    # Don't forget to use absolute paths, not relative paths.
-)
-
-INSTALLED_APPS = (
-    'django.contrib.auth',
-    'django.contrib.contenttypes',
-    'django.contrib.sessions',
-    'django.contrib.sites',
-    'django.contrib.messages',
-    'django.contrib.staticfiles',
-    # Uncomment the next line to enable the admin:
-    'django.contrib.admin',
-    # Uncomment the next line to enable admin documentation:
-    # 'django.contrib.admindocs',
-
-    'djcelery',
-    'djkombu',
-
-    'south',
-    'archive',
-)
-
-# A sample logging configuration. The only tangible logging
-# performed by this configuration is to send an email to
-# the site admins on every HTTP 500 error.
-# See http://docs.djangoproject.com/en/dev/topics/logging for
-# more details on how to customize your logging configuration.
-LOGGING = {
-    'version': 1,
-    'disable_existing_loggers': False,
-    'filters': {
-         'require_debug_false': {
-             '()': 'django.utils.log.RequireDebugFalse'
-         }
-     },
-    'handlers': {
-        'mail_admins': {
-            'level': 'ERROR',
-            'filters': ['require_debug_false'],
-            'class': 'django.utils.log.AdminEmailHandler'
-        }
-    },
-    'loggers': {
-        'django.request': {
-            'handlers': ['mail_admins'],
-            'level': 'ERROR',
-            'propagate': True,
-        },
-    }
-}
-
-#http://logowanie.nowoczesnapolska.org.pl/cas/'
-CAS_SERVER_URL = "http://logowanie.nowoczesnapolska.org.pl/cas/"
-CAS_VERSION = "1"
-
-
-EMAIL_SUBJECT_PREFIX = '[Audio] '
-SERVER_EMAIL = 'no-reply@audio.wolnelektury.pl'
-EMAIL_HOST = 'localhost'
-EMAIL_PORT = 25
-
-import djcelery
-djcelery.setup_loader()
-
-CELERY_SEND_TASK_ERROR_EMAILS = True
-BROKER_BACKEND = "djkombu.transport.DatabaseTransport"
-BROKER_HOST = "localhost"
-BROKER_PORT = 5672
-BROKER_USER = "guest"
-BROKER_PASSWORD = "guest"
-BROKER_VHOST = "/"
-
-
-try:
-    from localsettings import *
-except:
-    pass
diff --git a/audiobooks/urls.py b/audiobooks/urls.py
deleted file mode 100644 (file)
index f3113c8..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-from django.conf import settings
-from django.conf.urls import patterns, include, url
-from django.views.generic import RedirectView
-
-# Uncomment the next two lines to enable the admin:
-from django.contrib import admin
-admin.autodiscover()
-
-urlpatterns = patterns('',
-    url(r'^$', RedirectView.as_view(url='archive/')),
-    url(r'^archive/', include('archive.urls')),
-
-    url(r'^admin/', include(admin.site.urls)),
-    url(r'^accounts/login/$', 'django_cas.views.login', name='login'),
-    url(r'^accounts/logout/$', 'django_cas.views.logout', name='logout'),
-)
-
-if settings.DEBUG:
-    urlpatterns += patterns('',
-        (r'^media/(?P<path>.*)$', 'django.views.static.serve', {'document_root': settings.MEDIA_ROOT, 'show_indexes':True}),
-)
diff --git a/audiobooks/wsgi.py b/audiobooks/wsgi.py
deleted file mode 100644 (file)
index 5e0ddaa..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-"""
-WSGI config for edumed project.
-
-This module contains the WSGI application used by Django's development server
-and any production WSGI deployments. It should expose a module-level variable
-named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover
-this application via the ``WSGI_APPLICATION`` setting.
-
-Usually you will have the standard Django WSGI application here, but it also
-might make sense to replace the whole Django WSGI application with a custom one
-that later delegates to the Django one. For example, you could introduce WSGI
-middleware here, or combine a Django application with an application of another
-framework.
-
-"""
-import os
-
-os.environ.setdefault("DJANGO_SETTINGS_MODULE", "audiobooks.settings")
-
-# This application object is used by any WSGI server configured to use this
-# file. This includes Django's development server, if the WSGI_APPLICATION
-# setting points here.
-from django.core.wsgi import get_wsgi_application
-application = get_wsgi_application()
-
-# Apply WSGI middleware here.
-# from helloworld.wsgi import HelloWorldApplication
-# application = HelloWorldApplication(application)
diff --git a/src/archive/__init__.py b/src/archive/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/archive/admin.py b/src/archive/admin.py
new file mode 100644 (file)
index 0000000..d0804be
--- /dev/null
@@ -0,0 +1,5 @@
+from archive.models import Project, Audiobook
+from django.contrib import admin
+
+admin.site.register(Project)
+admin.site.register(Audiobook)
diff --git a/src/archive/constants.py b/src/archive/constants.py
new file mode 100644 (file)
index 0000000..17f4e78
--- /dev/null
@@ -0,0 +1,14 @@
+from django.utils.translation import ugettext_lazy as _
+
+class status:
+    WAITING = 1
+    ENCODING = 2
+    TAGGING = 3
+    SENDING = 4
+
+    choices = [
+        (WAITING, _('Waiting')),
+        (ENCODING, _('Encoding')),
+        (TAGGING, _('Tagging')),
+        (SENDING, _('Sending')),
+    ]
diff --git a/src/archive/cover.png b/src/archive/cover.png
new file mode 100644 (file)
index 0000000..1435a03
Binary files /dev/null and b/src/archive/cover.png differ
diff --git a/src/archive/forms.py b/src/archive/forms.py
new file mode 100644 (file)
index 0000000..81568c6
--- /dev/null
@@ -0,0 +1,42 @@
+from datetime import datetime
+import os
+import os.path
+
+from django import forms
+from django.utils.translation import ugettext_lazy as _
+import mutagen
+from django.utils.encoding import force_bytes
+
+from archive.models import Audiobook
+from archive.settings import FILES_PATH, NEW_PATH
+from archive.utils import ExistingFile, sha1_file
+
+class AudiobookForm(forms.ModelForm):
+    class Meta:
+        model = Audiobook
+
+    def save(self, commit=True, path=None):
+        """ Performs normal save, with given file as an source audiobook.
+
+            `path' is relative to NEW_PATH.
+        """
+        m = super(AudiobookForm, self).save(commit=False)
+        m.modified = datetime.now()
+
+        if path:
+            # adding a new audiobook
+            if not os.path.isdir(FILES_PATH):
+                os.makedirs(FILES_PATH)
+            # save the file in model
+
+            abs_path = os.path.join(NEW_PATH, path)
+            m.source_file.save(
+                path,
+                ExistingFile(abs_path))
+
+#            f = open(force_bytes(m.source_file.path))
+#            m.source_sha1 = sha1_file(f)
+#            f.close()
+
+        if commit:
+            m.save()
diff --git a/src/archive/locale/pl/LC_MESSAGES/django.mo b/src/archive/locale/pl/LC_MESSAGES/django.mo
new file mode 100644 (file)
index 0000000..e1135bf
Binary files /dev/null and b/src/archive/locale/pl/LC_MESSAGES/django.mo differ
diff --git a/src/archive/locale/pl/LC_MESSAGES/django.po b/src/archive/locale/pl/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..317ab76
--- /dev/null
@@ -0,0 +1,250 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) 2011 Fundacja Nowoczesna Polska
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2018-03-02 09:36+0100\n"
+"PO-Revision-Date: 2012-07-11 15:52+0100\n"
+"Last-Translator: Radek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
+"|| n%100>=20) ? 1 : 2)\n"
+
+#: constants.py:10
+msgid "Waiting"
+msgstr "W kolejce"
+
+#: constants.py:11
+msgid "Encoding"
+msgstr "Konwersja"
+
+#: constants.py:12
+msgid "Tagging"
+msgstr "Opisywanie"
+
+#: constants.py:13
+msgid "Sending"
+msgstr "WysyÅ‚anie"
+
+#: models.py:23 models.py:49
+msgid "project"
+msgstr "projekt"
+
+#: models.py:24
+msgid "projects"
+msgstr "projekty"
+
+#: models.py:37
+msgid "source file"
+msgstr "plik ÅºródÅ‚owy"
+
+#: models.py:40
+msgid "title"
+msgstr "tytuÅ‚"
+
+#: models.py:41
+msgid "part name"
+msgstr "nazwa części"
+
+#: models.py:41
+msgid "eg. chapter in a novel"
+msgstr "np. rozdziaÅ‚ w powieÅ›ci"
+
+#: models.py:43
+msgid "index"
+msgstr "numer"
+
+#: models.py:44
+msgid "parts count"
+msgstr "liczba części"
+
+#: models.py:45
+msgid "artist"
+msgstr "lektor"
+
+#: models.py:46
+msgid "conductor"
+msgstr "reżyser"
+
+#: models.py:47
+msgid "encoded by"
+msgstr "przyg. techn."
+
+#: models.py:48
+msgid "date"
+msgstr "data"
+
+#: models.py:50
+msgid "book url"
+msgstr "URL książki"
+
+#: models.py:51
+msgid "translator"
+msgstr "tÅ‚umacz"
+
+#: models.py:71
+msgid "audiobook"
+msgstr "audiobook"
+
+#: models.py:72
+msgid "audiobooks"
+msgstr "audiobooki"
+
+#: templates/base.html:7
+msgid "Audiobook repository"
+msgstr "Repozytorium audiobooków"
+
+#: templates/archive/base.html:5
+msgid "New"
+msgstr "Nowe"
+
+#: templates/archive/base.html:6
+msgid "Unpublished"
+msgstr "Nie opublikowane"
+
+#: templates/archive/base.html:7 templates/archive/file_managed.html:11
+msgid "Publishing"
+msgstr "Publikacja"
+
+#: templates/archive/base.html:8
+msgid "Published"
+msgstr "Opublikowane"
+
+#: templates/archive/base.html:9
+msgid "Archive"
+msgstr "Archiwum"
+
+#: templates/archive/base.html:11
+msgid "Projects"
+msgstr "Projekty"
+
+#: templates/archive/base.html:14
+msgid "Logout"
+msgstr "Wyloguj"
+
+#: templates/archive/base.html:16 templates/registration/login.html:9
+msgid "Login"
+msgstr "Zaloguj"
+
+#: templates/archive/base.html:19
+msgid "Administration"
+msgstr "Administracja"
+
+#: templates/archive/file_managed.html:15
+msgid "Publishing pending"
+msgstr "Czeka na publikacjÄ™"
+
+#: templates/archive/file_managed.html:19
+msgid "Cancel publishing"
+msgstr "Anuluj publikacjÄ™"
+
+#: templates/archive/file_managed.html:51
+msgid "Publish"
+msgstr "Opublikuj"
+
+#: templates/archive/file_managed.html:57
+msgid "Convert without publishing"
+msgstr "Konwertuj bez publikacji"
+
+#: templates/archive/file_managed.html:67
+msgid "MP3 file"
+msgstr "Plik MP3"
+
+#: templates/archive/file_managed.html:68
+msgid "Download MP3 file."
+msgstr "Pobierz plik MP3."
+
+#: templates/archive/file_managed.html:70
+#: templates/archive/file_managed.html:86
+msgid "Published:"
+msgstr "Opublikowano:"
+
+#: templates/archive/file_managed.html:75
+#: templates/archive/file_managed.html:91
+msgid "Not published yet."
+msgstr "Nie opublikowane."
+
+#: templates/archive/file_managed.html:78
+msgid "MP3 file hasn't been generated yet."
+msgstr "Plik MP3 nie zostaÅ‚ jeszcze wygenerowany."
+
+#: templates/archive/file_managed.html:83
+msgid "Ogg Vorbis file"
+msgstr "Plik Ogg Vorbis"
+
+#: templates/archive/file_managed.html:84
+msgid "Download Ogg Vorbis file."
+msgstr "Pobierz plik Ogg Vorbis."
+
+#: templates/archive/file_managed.html:94
+msgid "Ogg Vorbis file hasn't been generated yet."
+msgstr "Plik Ogg Vorbis nie zostaÅ‚ jeszcze wygenerowany."
+
+#: templates/archive/file_managed.html:105
+msgid "Update tags"
+msgstr "Uaktualnij tagi"
+
+#: templates/archive/file_managed.html:117 templates/archive/file_new.html:19
+msgid "Commit"
+msgstr "Zatwierdź"
+
+#: templates/archive/file_managed.html:128
+msgid "Are you sure you want to move this audiobook to archive?"
+msgstr "Czy na pewno chcesz przenieść ten plik to archiwum?"
+
+#: templates/archive/file_managed.html:130
+msgid "Remove to archive"
+msgstr "UsuÅ„ do archiwum"
+
+#: templates/archive/file_new.html:8
+msgid "Move to archive"
+msgstr "PrzenieÅ› do archiwum"
+
+#: templates/archive/file_unmanaged.html:6
+msgid "File with same name already exists!"
+msgstr "Plik o tej nazwie już istnieje!"
+
+#: templates/archive/file_unmanaged.html:28
+msgid "Move to new files"
+msgstr "PrzenieÅ› do nowych plików"
+
+#: templates/archive/list_new.html:6
+msgid "New audiobooks"
+msgstr "Nowe audiobooki"
+
+#: templates/archive/list_new.html:11
+msgid "Put source audiobooks in:"
+msgstr "Umieść nowe audiobooki w:"
+
+#: templates/archive/list_published.html:6
+msgid "Published audiobooks"
+msgstr "Opublikowane audiobooki"
+
+#: templates/archive/list_publishing.html:6
+msgid "Audiobooks being published"
+msgstr "Aktualnie publikowane audiobooki"
+
+#: templates/archive/list_unmanaged.html:5
+msgid "Unmanaged archive"
+msgstr "Audiobooki archiwalne"
+
+#: templates/archive/list_unpublished.html:6
+msgid "Unpublished audiobooks"
+msgstr "Nie opublikowane audiobooki"
+
+#~ msgid "arranger"
+#~ msgstr "aranżer"
+
+#~ msgid "Audiobook marked for publishing with tags:"
+#~ msgstr "Audiobook zaznaczony do publikacji z tagami:"
+
+#~ msgid "Publishing already in progress."
+#~ msgstr "Publikowanie rozpoczÄ™te."
diff --git a/src/archive/migrations/0001_initial.py b/src/archive/migrations/0001_initial.py
new file mode 100644 (file)
index 0000000..ac5a2c9
--- /dev/null
@@ -0,0 +1,80 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        
+        # Adding model 'Project'
+        db.create_table('archive_project', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=128, db_index=True)),
+            ('sponsors', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+        ))
+        db.send_create_signal('archive', ['Project'])
+
+        # Adding model 'Audiobook'
+        db.create_table('archive_audiobook', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('source_file', self.gf('django.db.models.fields.files.FileField')(max_length=100)),
+            ('title', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('artist', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('conductor', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('encoded_by', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('date', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('project', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['archive.Project'])),
+            ('url', self.gf('django.db.models.fields.URLField')(max_length=255)),
+            ('modified', self.gf('django.db.models.fields.DateTimeField')(null=True)),
+            ('published_tags', self.gf('jsonfield.fields.JSONField')(null=True)),
+            ('mp3_file', self.gf('django.db.models.fields.files.FileField')(max_length=100, null=True)),
+            ('ogg_file', self.gf('django.db.models.fields.files.FileField')(max_length=100, null=True)),
+            ('publishing_tags', self.gf('jsonfield.fields.JSONField')(null=True)),
+            ('publish_wait', self.gf('django.db.models.fields.DateTimeField')(null=True)),
+            ('publishing', self.gf('django.db.models.fields.BooleanField')(default=False)),
+            ('published', self.gf('django.db.models.fields.DateTimeField')(null=True)),
+        ))
+        db.send_create_signal('archive', ['Audiobook'])
+
+
+    def backwards(self, orm):
+        
+        # Deleting model 'Project'
+        db.delete_table('archive_project')
+
+        # Deleting model 'Audiobook'
+        db.delete_table('archive_audiobook')
+
+
+    models = {
+        'archive.audiobook': {
+            'Meta': {'ordering': "('title',)", 'object_name': 'Audiobook'},
+            'artist': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'conductor': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'encoded_by': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'modified': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+            'mp3_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True'}),
+            'ogg_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True'}),
+            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['archive.Project']"}),
+            'publish_wait': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+            'published': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+            'published_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
+            'publishing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'publishing_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
+            'source_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'url': ('django.db.models.fields.URLField', [], {'max_length': '255'})
+        },
+        'archive.project': {
+            'Meta': {'ordering': "('name',)", 'object_name': 'Project'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'}),
+            'sponsors': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        }
+    }
+
+    complete_apps = ['archive']
diff --git a/src/archive/migrations/0002_auto__del_field_audiobook_publishing_tags__del_field_audiobook_publish.py b/src/archive/migrations/0002_auto__del_field_audiobook_publishing_tags__del_field_audiobook_publish.py
new file mode 100644 (file)
index 0000000..03cb130
--- /dev/null
@@ -0,0 +1,139 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        
+        # Deleting field 'Audiobook.publishing_tags'
+        db.delete_column('archive_audiobook', 'publishing_tags')
+
+        # Deleting field 'Audiobook.published_tags'
+        db.delete_column('archive_audiobook', 'published_tags')
+
+        # Deleting field 'Audiobook.publish_wait'
+        db.delete_column('archive_audiobook', 'publish_wait')
+
+        # Deleting field 'Audiobook.published'
+        db.delete_column('archive_audiobook', 'published')
+
+        # Deleting field 'Audiobook.publishing'
+        db.delete_column('archive_audiobook', 'publishing')
+
+        # Adding field 'Audiobook.mp3_status'
+        db.add_column('archive_audiobook', 'mp3_status', self.gf('django.db.models.fields.SmallIntegerField')(null=True), keep_default=False)
+
+        # Adding field 'Audiobook.mp3_task'
+        db.add_column('archive_audiobook', 'mp3_task', self.gf('django.db.models.fields.CharField')(max_length=64, null=True), keep_default=False)
+
+        # Adding field 'Audiobook.mp3_tags'
+        db.add_column('archive_audiobook', 'mp3_tags', self.gf('jsonfield.fields.JSONField')(null=True), keep_default=False)
+
+        # Adding field 'Audiobook.mp3_published_tags'
+        db.add_column('archive_audiobook', 'mp3_published_tags', self.gf('jsonfield.fields.JSONField')(null=True), keep_default=False)
+
+        # Adding field 'Audiobook.mp3_published'
+        db.add_column('archive_audiobook', 'mp3_published', self.gf('django.db.models.fields.DateTimeField')(null=True), keep_default=False)
+
+        # Adding field 'Audiobook.ogg_status'
+        db.add_column('archive_audiobook', 'ogg_status', self.gf('django.db.models.fields.SmallIntegerField')(null=True), keep_default=False)
+
+        # Adding field 'Audiobook.ogg_task'
+        db.add_column('archive_audiobook', 'ogg_task', self.gf('django.db.models.fields.CharField')(max_length=64, null=True), keep_default=False)
+
+        # Adding field 'Audiobook.ogg_tags'
+        db.add_column('archive_audiobook', 'ogg_tags', self.gf('jsonfield.fields.JSONField')(null=True), keep_default=False)
+
+        # Adding field 'Audiobook.ogg_published_tags'
+        db.add_column('archive_audiobook', 'ogg_published_tags', self.gf('jsonfield.fields.JSONField')(null=True), keep_default=False)
+
+        # Adding field 'Audiobook.ogg_published'
+        db.add_column('archive_audiobook', 'ogg_published', self.gf('django.db.models.fields.DateTimeField')(null=True), keep_default=False)
+
+
+    def backwards(self, orm):
+        
+        # Adding field 'Audiobook.publishing_tags'
+        db.add_column('archive_audiobook', 'publishing_tags', self.gf('jsonfield.fields.JSONField')(null=True), keep_default=False)
+
+        # Adding field 'Audiobook.published_tags'
+        db.add_column('archive_audiobook', 'published_tags', self.gf('jsonfield.fields.JSONField')(null=True), keep_default=False)
+
+        # Adding field 'Audiobook.publish_wait'
+        db.add_column('archive_audiobook', 'publish_wait', self.gf('django.db.models.fields.DateTimeField')(null=True), keep_default=False)
+
+        # Adding field 'Audiobook.published'
+        db.add_column('archive_audiobook', 'published', self.gf('django.db.models.fields.DateTimeField')(null=True), keep_default=False)
+
+        # Adding field 'Audiobook.publishing'
+        db.add_column('archive_audiobook', 'publishing', self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=False)
+
+        # Deleting field 'Audiobook.mp3_status'
+        db.delete_column('archive_audiobook', 'mp3_status')
+
+        # Deleting field 'Audiobook.mp3_task'
+        db.delete_column('archive_audiobook', 'mp3_task')
+
+        # Deleting field 'Audiobook.mp3_tags'
+        db.delete_column('archive_audiobook', 'mp3_tags')
+
+        # Deleting field 'Audiobook.mp3_published_tags'
+        db.delete_column('archive_audiobook', 'mp3_published_tags')
+
+        # Deleting field 'Audiobook.mp3_published'
+        db.delete_column('archive_audiobook', 'mp3_published')
+
+        # Deleting field 'Audiobook.ogg_status'
+        db.delete_column('archive_audiobook', 'ogg_status')
+
+        # Deleting field 'Audiobook.ogg_task'
+        db.delete_column('archive_audiobook', 'ogg_task')
+
+        # Deleting field 'Audiobook.ogg_tags'
+        db.delete_column('archive_audiobook', 'ogg_tags')
+
+        # Deleting field 'Audiobook.ogg_published_tags'
+        db.delete_column('archive_audiobook', 'ogg_published_tags')
+
+        # Deleting field 'Audiobook.ogg_published'
+        db.delete_column('archive_audiobook', 'ogg_published')
+
+
+    models = {
+        'archive.audiobook': {
+            'Meta': {'ordering': "('title',)", 'object_name': 'Audiobook'},
+            'artist': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'conductor': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'encoded_by': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'modified': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+            'mp3_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True'}),
+            'mp3_published': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+            'mp3_published_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
+            'mp3_status': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True'}),
+            'mp3_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
+            'mp3_task': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}),
+            'ogg_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True'}),
+            'ogg_published': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+            'ogg_published_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
+            'ogg_status': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True'}),
+            'ogg_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
+            'ogg_task': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}),
+            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['archive.Project']"}),
+            'source_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'url': ('django.db.models.fields.URLField', [], {'max_length': '255'})
+        },
+        'archive.project': {
+            'Meta': {'ordering': "('name',)", 'object_name': 'Project'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'}),
+            'sponsors': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        }
+    }
+
+    complete_apps = ['archive']
diff --git a/src/archive/migrations/0003_auto__add_field_audiobook_source_sha1__add_field_audiobook_translator.py b/src/archive/migrations/0003_auto__add_field_audiobook_source_sha1__add_field_audiobook_translator.py
new file mode 100644 (file)
index 0000000..9711b72
--- /dev/null
@@ -0,0 +1,84 @@
+# encoding: utf-8
+import datetime
+from hashlib import sha1
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+def sha1_file(f):
+    sha = sha1()
+    for piece in iter(lambda: f.read(1024*1024), ''):
+        sha.update(piece)
+    return sha.hexdigest()
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        
+        # Adding field 'Audiobook.source_sha1'
+        db.add_column('archive_audiobook', 'source_sha1', self.gf('django.db.models.fields.CharField')(default='', max_length=40), keep_default=False)
+
+        # Adding field 'Audiobook.translator'
+        db.add_column('archive_audiobook', 'translator', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True), keep_default=False)
+
+        if not db.dry_run:
+            for obj in orm.Audiobook.objects.all():
+                try:
+                    f = open(obj.source_file.path)
+                except ValueError, e:
+                    print "Audiobook has no source file"
+                    pass
+                else:
+                    obj.source_sha1 = sha1_file(f)
+                    f.close()
+                    obj.save()
+
+
+    def backwards(self, orm):
+        
+        # Deleting field 'Audiobook.source_sha1'
+        db.delete_column('archive_audiobook', 'source_sha1')
+
+        # Deleting field 'Audiobook.translator'
+        db.delete_column('archive_audiobook', 'translator')
+
+
+    models = {
+        'archive.audiobook': {
+            'Meta': {'ordering': "('title',)", 'object_name': 'Audiobook'},
+            'artist': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'conductor': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'encoded_by': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'modified': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+            'mp3_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True'}),
+            'mp3_published': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+            'mp3_published_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
+            'mp3_status': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True'}),
+            'mp3_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
+            'mp3_task': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}),
+            'ogg_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True'}),
+            'ogg_published': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+            'ogg_published_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
+            'ogg_status': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True'}),
+            'ogg_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
+            'ogg_task': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}),
+            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['archive.Project']"}),
+            'source_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+            'source_sha1': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'translator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'url': ('django.db.models.fields.URLField', [], {'max_length': '255'})
+        },
+        'archive.project': {
+            'Meta': {'ordering': "('name',)", 'object_name': 'Project'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'}),
+            'sponsors': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        }
+    }
+
+    complete_apps = ['archive']
diff --git a/src/archive/migrations/0004_auto__chg_field_audiobook_source_file.py b/src/archive/migrations/0004_auto__chg_field_audiobook_source_file.py
new file mode 100644 (file)
index 0000000..95d630e
--- /dev/null
@@ -0,0 +1,57 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        
+        # Changing field 'Audiobook.source_file'
+        db.alter_column('archive_audiobook', 'source_file', self.gf('django.db.models.fields.files.FileField')(max_length=255))
+
+
+    def backwards(self, orm):
+        
+        # Changing field 'Audiobook.source_file'
+        db.alter_column('archive_audiobook', 'source_file', self.gf('django.db.models.fields.files.FileField')(max_length=100))
+
+
+    models = {
+        'archive.audiobook': {
+            'Meta': {'ordering': "('title',)", 'object_name': 'Audiobook'},
+            'artist': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'conductor': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'encoded_by': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'modified': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+            'mp3_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True'}),
+            'mp3_published': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+            'mp3_published_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
+            'mp3_status': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True'}),
+            'mp3_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
+            'mp3_task': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}),
+            'ogg_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True'}),
+            'ogg_published': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+            'ogg_published_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
+            'ogg_status': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True'}),
+            'ogg_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
+            'ogg_task': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}),
+            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['archive.Project']"}),
+            'source_file': ('django.db.models.fields.files.FileField', [], {'max_length': '255'}),
+            'source_sha1': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'translator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'url': ('django.db.models.fields.URLField', [], {'max_length': '255'})
+        },
+        'archive.project': {
+            'Meta': {'ordering': "('name',)", 'object_name': 'Project'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'}),
+            'sponsors': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        }
+    }
+
+    complete_apps = ['archive']
diff --git a/src/archive/migrations/0005_auto__add_field_audiobook_part_name__add_field_audiobook_index__add_fi.py b/src/archive/migrations/0005_auto__add_field_audiobook_part_name__add_field_audiobook_index__add_fi.py
new file mode 100644 (file)
index 0000000..c163971
--- /dev/null
@@ -0,0 +1,77 @@
+# -*- coding: utf-8 -*-
+from south.utils import datetime_utils as datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Adding field 'Audiobook.part_name'
+        db.add_column(u'archive_audiobook', 'part_name',
+                      self.gf('django.db.models.fields.CharField')(default='', max_length=255, blank=True),
+                      keep_default=False)
+
+        # Adding field 'Audiobook.index'
+        db.add_column(u'archive_audiobook', 'index',
+                      self.gf('django.db.models.fields.IntegerField')(default=0),
+                      keep_default=False)
+
+        # Adding field 'Audiobook.parts_count'
+        db.add_column(u'archive_audiobook', 'parts_count',
+                      self.gf('django.db.models.fields.IntegerField')(default=1),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+        # Deleting field 'Audiobook.part_name'
+        db.delete_column(u'archive_audiobook', 'part_name')
+
+        # Deleting field 'Audiobook.index'
+        db.delete_column(u'archive_audiobook', 'index')
+
+        # Deleting field 'Audiobook.parts_count'
+        db.delete_column(u'archive_audiobook', 'parts_count')
+
+
+    models = {
+        u'archive.audiobook': {
+            'Meta': {'ordering': "('title',)", 'object_name': 'Audiobook'},
+            'artist': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'conductor': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'date': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'encoded_by': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'index': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'modified': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+            'mp3_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True'}),
+            'mp3_published': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+            'mp3_published_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
+            'mp3_status': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True'}),
+            'mp3_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
+            'mp3_task': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}),
+            'ogg_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True'}),
+            'ogg_published': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+            'ogg_published_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
+            'ogg_status': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True'}),
+            'ogg_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}),
+            'ogg_task': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}),
+            'part_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'blank': 'True'}),
+            'parts_count': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['archive.Project']"}),
+            'source_file': ('django.db.models.fields.files.FileField', [], {'max_length': '255'}),
+            'source_sha1': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'translator': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'url': ('django.db.models.fields.URLField', [], {'max_length': '255'})
+        },
+        u'archive.project': {
+            'Meta': {'ordering': "('name',)", 'object_name': 'Project'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'}),
+            'sponsors': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+        }
+    }
+
+    complete_apps = ['archive']
\ No newline at end of file
diff --git a/src/archive/migrations/__init__.py b/src/archive/migrations/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/archive/models.py b/src/archive/models.py
new file mode 100644 (file)
index 0000000..c75a874
--- /dev/null
@@ -0,0 +1,139 @@
+# -*- coding: utf-8 -*-
+import os.path
+
+from django.db import models
+from time import sleep
+from jsonfield.fields import JSONField
+from django.utils.encoding import force_bytes
+from django.utils.translation import ugettext_lazy as _
+from archive.constants import status
+from archive.settings import FILES_SAVE_PATH, ADVERT, LICENSE, ORGANIZATION, PROJECT
+from archive.utils import OverwriteStorage, sha1_file
+
+# Create your models here.
+
+
+class Project(models.Model):
+    """ an audiobook project, needed for specyfing sponsors """
+
+    name = models.CharField(max_length=128, unique=True, db_index=True, verbose_name="Nazwa")
+    sponsors = models.TextField(blank=True, null=True, verbose_name="Sponsorzy")
+
+    class Meta:
+        verbose_name = _("project")
+        verbose_name_plural = _("projects")
+        ordering = ("name",)
+
+    def __unicode__(self):
+        return self.name
+
+
+def source_upload_to(intance, filename):
+    return os.path.join(FILES_SAVE_PATH, filename) # FIXME: what about really long file names?
+
+
+class Audiobook(models.Model):
+    source_file = models.FileField(upload_to=source_upload_to, max_length=255, 
+            verbose_name=_('source file'), editable=False)
+    source_sha1 = models.CharField(max_length=40, editable=False)
+
+    title = models.CharField(max_length=255, verbose_name=_('title'))
+    part_name = models.CharField(max_length=255, verbose_name=_('part name'), help_text=_('eg. chapter in a novel'),
+                                 default='', blank=True)
+    index = models.IntegerField(verbose_name=_('index'), default=0)
+    parts_count = models.IntegerField(verbose_name=_('parts count'), default=1)
+    artist = models.CharField(max_length=255, verbose_name=_('artist'))
+    conductor = models.CharField(max_length=255, verbose_name=_('conductor'))
+    encoded_by = models.CharField(max_length=255, verbose_name=_('encoded by'))
+    date = models.CharField(max_length=255, verbose_name=_('date'))
+    project = models.ForeignKey(Project, verbose_name=_('project'))
+    url = models.URLField(max_length=255, verbose_name=_('book url'))
+    translator = models.CharField(max_length=255, null=True, blank=True, verbose_name=_('translator'))
+    modified = models.DateTimeField(null=True, editable=False)
+
+    # publishing process
+    mp3_status = models.SmallIntegerField(null=True, editable=False, choices=status.choices)
+    mp3_task = models.CharField(max_length=64, null=True, editable=False)
+    mp3_tags = JSONField(null=True, editable=False)
+    mp3_file = models.FileField(null=True, upload_to='archive/final', storage=OverwriteStorage(), editable=False)
+    mp3_published_tags = JSONField(null=True, editable=False)
+    mp3_published = models.DateTimeField(null=True, editable=False)
+
+    ogg_status = models.SmallIntegerField(null=True, editable=False, choices=status.choices)
+    ogg_task = models.CharField(max_length=64, null=True, editable=False)
+    ogg_tags = JSONField(null=True, editable=False)
+    ogg_file = models.FileField(null=True, upload_to='archive/final', storage=OverwriteStorage(), editable=False)
+    ogg_published_tags = JSONField(null=True, editable=False)
+    ogg_published = models.DateTimeField(null=True, editable=False)
+
+
+    class Meta:
+        verbose_name = _("audiobook")
+        verbose_name_plural = _("audiobooks")
+        ordering = ("title",)
+
+    def __unicode__(self):
+        return self.title
+
+    def published(self):
+        return self.mp3_published and self.ogg_published
+
+    def get_source_sha1(self):
+        source_sha1 = self.source_sha1
+        if self.pk:
+            source_sha1 = type(self).objects.get(pk=self.pk).source_sha1
+            while source_sha1 == 'wait':
+                sleep(10)
+        if not source_sha1:
+            self.source_sha1 = 'wait'
+            if self.pk:
+                type(self).objects.filter(pk=self.pk).update(source_sha1='wait')
+            try:
+                f = open(force_bytes(self.source_file.path))
+                source_sha1 = sha1_file(f)
+                self.source_sha1 = source_sha1
+                if self.pk:
+                    type(self).objects.filter(pk=self.pk).update(source_sha1=source_sha1)
+            except:
+                self.source_sha1 = ''
+                if self.pk:
+                    type(self).objects.filter(pk=self.pk).update(source_sha1='')
+                return None
+        return source_sha1
+
+
+    def new_publish_tags(self):
+        title = self.title
+        if self.translator:
+            title += u' (tÅ‚um. %s)' % self.translator
+
+        copyright = u"%s %s. Licensed to the public under %s verify at %s" % (
+                self.date, ORGANIZATION, LICENSE, self.url)
+
+        comment = u"Audiobook nagrany w ramach projektu %s%s.\n%s" % (
+                    self.project.name,
+                    u" finansowanego przez %s" % self.project.sponsors if self.project.sponsors else "",
+                    ADVERT)
+
+        tags = {
+            'album': PROJECT,
+            'albumartist': ORGANIZATION,
+            'artist': self.artist,
+            'comment': comment,
+            'conductor': self.conductor,
+            'contact': self.url,
+            'copyright': copyright,
+            'date': self.date,
+            'genre': u'Speech',
+            'language': u'pol',
+            'license': LICENSE,
+            'organization': ORGANIZATION,
+            'title': title,
+            #'flac_sha1': self.get_source_sha1(),
+            'project': self.project.name,
+            'funded_by': self.project.sponsors,
+        }
+        if self.source_sha1 and self.source_sha1 != 'wait':
+            tags['flac_sha1'] = self.source_sha1
+        return tags
+
diff --git a/src/archive/settings.py b/src/archive/settings.py
new file mode 100644 (file)
index 0000000..476f6c7
--- /dev/null
@@ -0,0 +1,108 @@
+# -*- coding: utf-8 -*-
+
+import os.path
+from django.conf import settings
+
+# this is where the end user puts new files
+try:
+    NEW_PATH = settings.ARCHIVE_NEW_PATH
+except AttributeError:
+    NEW_PATH = os.path.abspath(os.path.join(settings.MEDIA_ROOT,
+                        "archive/new"))
+
+# here the application keeps its managed files
+try:
+    FILES_PATH = settings.ARCHIVE_FILES_PATH
+except AttributeError:
+    FILES_PATH = os.path.abspath(os.path.join(settings.MEDIA_ROOT,
+                        "archive/files"))
+
+if FILES_PATH.startswith(settings.MEDIA_ROOT):
+    FILES_SAVE_PATH = FILES_PATH[len(settings.MEDIA_ROOT):].lstrip('/')
+
+
+# here the app keeps the unmanaged (archive) files
+try:
+    UNMANAGED_PATH = settings.ARCHIVE_UNMANAGED_PATH
+except AttributeError:
+    UNMANAGED_PATH = os.path.abspath(os.path.join(settings.MEDIA_ROOT,
+                        "archive/unmanaged"))
+
+
+# here the app keeps the resulting (published) files
+try:
+    FINAL_PATH = settings.ARCHIVE_FINAL_PATH
+except AttributeError:
+    FINAL_PATH = os.path.abspath(os.path.join(settings.MEDIA_ROOT,
+                        "archive/final"))
+
+
+# here the app keeps temporary build files
+try:
+    BUILD_PATH = settings.ARCHIVE_BUILD_PATH
+except AttributeError:
+    BUILD_PATH = os.path.abspath(os.path.join(settings.MEDIA_ROOT,
+                        "archive/build"))
+
+# upload conf
+try:
+    UPLOAD_HOST = settings.ARCHIVE_UPLOAD_HOST
+except AttributeError:
+    UPLOAD_HOST = 'wolnelektury.pl'
+
+try:
+    UPLOAD_USER = settings.ARCHIVE_UPLOAD_USER
+except AttributeError:
+    UPLOAD_USER = 'username'
+
+try:
+    UPLOAD_PASSWORD = settings.ARCHIVE_UPLOAD_PASSWORD
+except AttributeError:
+    UPLOAD_PASSWORD = None
+
+try:
+    UPLOAD_PATH = settings.ARCHIVE_UPLOAD_PATH
+except AttributeError:
+    UPLOAD_PATH = ''
+
+try:
+    UPLOAD_CMD = settings.ARCHIVE_UPLOAD_CMD
+except AttributeError:
+    UPLOAD_CMD = '/path/to/manage.py savemedia'
+
+try:
+    UPLOAD_SUDO = settings.ARCHIVE_UPLOAD_SUDO
+except AttributeError:
+    UPLOAD_SUDO = None
+
+
+try:
+    PROJECT = settings.ARCHIVE_PROJECT
+except AttributeError:
+    PROJECT = u'Wolne Lektury'
+
+try:
+    LICENSE = settings.ARCHIVE_LICENSE
+except AttributeError:
+    LICENSE = u'http://creativecommons.org/licenses/by-sa/3.0/deed.pl'
+
+try:
+    ORGANIZATION = settings.ARCHIVE_ORGANIZATION
+except AttributeError:
+    ORGANIZATION = u'Fundacja Nowoczesna Polska'
+
+try:
+    ADVERT = settings.ARCHIVE_ADVERT
+except AttributeError:
+    ADVERT = u"""
+Przekaż 1% podatku na rozwój Wolnych Lektur:
+Fundacja Nowoczesna Polska
+KRS 0000070056
+http://nowoczesnapolska.org.pl/wesprzyj_nas/
+"""
+
+try:
+    COVER_IMAGE = settings.ARCHIVE_COVER_IMAGE
+except AttributeError:
+    COVER_IMAGE = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'cover.png')
+
diff --git a/src/archive/static/style.css b/src/archive/static/style.css
new file mode 100755 (executable)
index 0000000..434b87b
--- /dev/null
@@ -0,0 +1,67 @@
+body {
+    margin: 0;
+    font-family: verdana, sans-serif;
+    font-size: 12px;
+}
+
+a {
+    color: #bf6000;
+    text-decoration: none;
+}
+
+.clr {
+    clear: both;
+}
+
+
+#repo-zones-nav {
+    padding: 5px 5px 0 10px;
+    background: #ffdfbf;
+    border-bottom: 1px solid #ff8000;
+}
+
+#repo-zones-nav a, #repo-zones-nav span {
+    display: block;
+    float: left;
+    padding: 5px 20px 5px 20px;
+    margin-bottom: -1px;
+    border-width: 1px;
+    border-style: solid;
+    border-color: rgba(0,0,0,0);
+}
+
+#repo-zones-nav .active {
+    background: white;
+    border-color: #ff8000 #ff8000 white #ff8000;
+}
+
+#content {
+    padding: 10px;
+}
+
+
+.errorlist {
+    margin: 0;
+}
+
+.errorlist li {
+    color: red;
+}
+
+
+.tags, .multiple_tags {
+    border-collapse: collapse;
+}
+
+.tags td, .tags th, .multiple_tags td, .multiple_tags th {
+    border: 1px solid #ddd;
+    padding: 3px;
+}
+
+
+.list-published-tag, .list-publishing-tag {
+    font-size: .8em;
+}
+.list-publishing-tag {
+    color: #aaa;
+}
diff --git a/src/archive/tasks.py b/src/archive/tasks.py
new file mode 100644 (file)
index 0000000..a46b79c
--- /dev/null
@@ -0,0 +1,214 @@
+from datetime import datetime
+import errno
+import mimetypes
+import os
+import os.path
+import pipes
+import stat
+import subprocess
+from tempfile import NamedTemporaryFile
+from time import sleep
+
+#from celery.decorators import task
+from celery.task import Task
+from django.db.models import F
+from fabric import api
+from fabric.network import disconnect_all
+from mutagen import File
+from mutagen import id3
+
+import mutagen
+
+from archive.constants import status
+from archive.models import Audiobook
+from archive.settings import (BUILD_PATH, COVER_IMAGE,
+    UPLOAD_HOST, UPLOAD_USER, UPLOAD_PASSWORD, UPLOAD_PATH, UPLOAD_CMD, UPLOAD_SUDO)
+from archive.utils import ExistingFile
+
+api.env.host_string = UPLOAD_HOST
+api.env.user = UPLOAD_USER
+api.env.password = UPLOAD_PASSWORD
+
+class AudioFormatTask(Task):
+    abstract = True
+
+    class RemoteOperationError(BaseException):
+        pass
+
+    @classmethod
+    def set_status(cls, aid, status):
+        Audiobook.objects.filter(pk=aid).update(
+            **{'%s_status' % cls.ext: status})
+
+    @staticmethod
+    def encode(in_path, out_path):
+        raise NotImplemented
+
+    @classmethod
+    def set_tags(cls, audiobook, file_name):
+        tags = getattr(audiobook, "%s_tags" % cls.ext)['tags']
+        if not tags.get('flac_sha1'):
+            tags['flac_sha1'] = audiobook.get_source_sha1()
+        audio = File(file_name)
+        for k, v in tags.items():
+            audio[k] = v
+        audio.save()
+
+    @classmethod
+    def save(cls, audiobook, file_name):
+        field = "%s_file" % cls.ext
+        getattr(audiobook, field).save(
+            "%d.%s" % (audiobook.pk, cls.ext),
+            ExistingFile(file_name),
+            save=False
+            )
+        os.chmod(getattr(audiobook, field).path, stat.S_IREAD|stat.S_IWRITE|stat.S_IRGRP|stat.S_IROTH)
+        Audiobook.objects.filter(pk=audiobook.pk).update(
+            **{field: getattr(audiobook, field)})
+
+    @classmethod
+    def published(cls, aid):
+        kwargs = {
+            "%s_published_tags" % cls.ext: F("%s_tags" % cls.ext),
+            "%s_tags" % cls.ext: None,
+            "%s_published" % cls.ext: datetime.now(),
+            '%s_status' % cls.ext: None,
+        }
+        Audiobook.objects.filter(pk=aid).update(**kwargs)
+
+    @classmethod
+    def put(cls, audiobook, path):
+        tags = getattr(audiobook, "%s_tags" % cls.ext)
+        prefix, slug = tags['url'].rstrip('/').rsplit('/', 1)
+        name = tags['name']
+        command = UPLOAD_CMD + (u' %s %s %s %s %s %s > output.txt' % (
+            pipes.quote(os.path.join(UPLOAD_PATH, os.path.basename(path))),
+            pipes.quote(slug),
+            pipes.quote(name),
+            pipes.quote(audiobook.part_name),
+            audiobook.index,
+            audiobook.parts_count,
+            )).encode('utf-8')
+        try:
+            api.put(path, UPLOAD_PATH)
+            if UPLOAD_SUDO:
+                api.sudo(command, user=UPLOAD_SUDO, shell=False)
+            else:
+                api.run(command)
+            disconnect_all()
+        except SystemExit, e:
+            raise cls.RemoteOperationError
+
+    def run(self, aid, publish=True):
+        aid = int(aid)
+        audiobook = Audiobook.objects.get(id=aid)
+        self.set_status(aid, status.ENCODING)
+
+        try:
+            os.makedirs(BUILD_PATH)
+        except OSError as e:
+            if e.errno == errno.EEXIST:
+                pass
+            else:
+                raise
+
+        out_file = NamedTemporaryFile(delete=False, prefix='%d-' % aid, suffix='.%s' % self.ext, dir=BUILD_PATH)
+        out_file.close()
+        self.encode(audiobook.source_file.path, out_file.name)
+        self.set_status(aid, status.TAGGING)
+        self.set_tags(audiobook, out_file.name)
+        self.set_status(aid, status.SENDING)
+
+        if publish:
+            self.put(audiobook, out_file.name)
+            self.published(aid)
+        else:
+            self.set_status(aid, None)
+
+        self.save(audiobook, out_file.name)
+
+    def on_failure(self, exc, task_id, args, kwargs, einfo):
+        aid = (args[0], kwargs.get('aid'))[0]
+        self.set_status(aid, None)
+
+
+class Mp3Task(AudioFormatTask):
+    ext = 'mp3'
+
+    # these shouldn't be staticmethods
+    def id3_text(tag, text):
+        return tag(encoding=1, text=text)
+    def id3_url(tag, text):
+        return tag(url=text)
+    def id3_comment(tag, text, lang=u'pol'):
+        return tag(encoding=1, lang=lang, desc=u'', text=text)
+    def id3_priv(tag, text, what=u''):
+        return tag(owner='wolnelektury.pl?%s' % what, data=text.encode('utf-8'))
+
+    TAG_MAP = {
+        'album': (id3_text, id3.TALB),
+        'albumartist': (id3_text, id3.TPE2),
+        'artist': (id3_text, id3.TPE1),
+        'conductor': (id3_text, id3.TPE3),
+        'copyright': (id3_text, id3.TCOP),
+        'date': (id3_text, id3.TDRC),
+        'genre': (id3_text, id3.TCON),
+        'language': (id3_text, id3.TLAN),
+        'organization': (id3_text, id3.TPUB),
+        'title': (id3_text, id3.TIT2),
+        'comment': (id3_comment, id3.COMM, 'pol'),
+        'contact': (id3_url, id3.WOAF),
+        'license': (id3_url, id3.WCOP),
+        'flac_sha1': (id3_priv, id3.PRIV, 'flac_sha1'),
+        'project': (id3_priv, id3.PRIV, 'project'),
+        'funded_by': (id3_priv, id3.PRIV, 'funded_by'),
+    }
+
+    @staticmethod
+    def encode(in_path, out_path):
+        # 44.1kHz 64kbps mono MP3
+        subprocess.check_call(['ffmpeg', 
+            '-i', in_path.encode('utf-8'),
+            '-ar', '44100',
+            '-ab', '64k',
+            '-ac', '1',
+            '-y',
+            '-acodec', 'libmp3lame',
+            out_path.encode('utf-8')
+            ])
+
+    @classmethod
+    def set_tags(cls, audiobook, file_name):
+        mp3_tags = audiobook.mp3_tags['tags']
+        if not mp3_tags.get('flac_sha1'):
+            mp3_tags['flac_sha1'] = audiobook.get_source_sha1()
+        audio = id3.ID3(file_name)
+        for k, v in mp3_tags.items():
+            factory_tuple = cls.TAG_MAP[k]
+            factory, tagtype = factory_tuple[:2]
+            audio.add(factory(tagtype, v, *factory_tuple[2:]))
+
+        if COVER_IMAGE:
+            mime = mimetypes.guess_type(COVER_IMAGE)
+            f = open(COVER_IMAGE)
+            audio.add(id3.APIC(encoding=0, mime=mime, type=3, desc=u'', data=f.read()))
+            f.close()
+
+        audio.save()
+
+
+class OggTask(AudioFormatTask):
+    ext = 'ogg'
+
+    @staticmethod
+    def encode(in_path, out_path):
+        # 44.1kHz 64kbps mono Ogg Vorbis
+        subprocess.check_call(['ffmpeg', 
+            '-i', in_path.encode('utf-8'),
+            '-ar', '44100',
+            '-ab', '64k',
+            '-ac', '1',
+            '-y',
+            '-acodec', 'libvorbis',
+            out_path.encode('utf-8')
+            ])
diff --git a/src/archive/templates/404.html b/src/archive/templates/404.html
new file mode 100644 (file)
index 0000000..467dacd
--- /dev/null
@@ -0,0 +1,6 @@
+{% extends "archive/base.html" %}
+{% block content %}
+<h1>Nie znaleziono strony.</h1>
+
+<p>Nie znaleziono Å¼Ä…danej strony.</p>
+{% endblock %}
diff --git a/src/archive/templates/500.html b/src/archive/templates/500.html
new file mode 100644 (file)
index 0000000..b46280d
--- /dev/null
@@ -0,0 +1 @@
+BÅ‚Ä…d serwera.
diff --git a/src/archive/templates/archive/base.html b/src/archive/templates/archive/base.html
new file mode 100644 (file)
index 0000000..8ec34e9
--- /dev/null
@@ -0,0 +1,25 @@
+{% extends "base.html" %}
+{% load i18n %}
+
+{% block repo-zones-nav %}
+    <a {% if division = "new" %}class="active" {% endif %}href="{% url 'list_new' %}">{% trans "New" %}</a>
+    <a {% if division = "unpublished" %}class="active" {% endif %}href="{% url 'list_unpublished' %}">{% trans "Unpublished" %}</a>
+    <a {% if division = "publishing" %}class="active" {% endif %}href="{% url 'list_publishing' %}">{% trans "Publishing" %}</a>
+    <a {% if division = "published" %}class="active" {% endif %}href="{% url 'list_published' %}">{% trans "Published" %}</a>
+    <a {% if division = "unmanaged" %}class="active" {% endif %}href="{% url 'list_unmanaged' %}">{% trans "Archive" %}</a>
+    {% if user.is_staff %}
+        <a href='{% url "admin:archive_project_changelist" %}'>{% trans "Projects" %}</a>
+    {% endif %}
+    {% if user.is_authenticated %}
+        <a href="{% url 'logout' %}" style='float: right;'>{% trans "Logout" %}</a>
+    {% else %}
+        <a href="{% url 'login' %}" style='float: right;'>{% trans "Login" %}</a>
+    {% endif %}
+    {% if user.is_staff %}
+        <a href='{% url "admin:index" %}' style='float: right;'>{% trans "Administration" %}</a>
+    {% endif %}
+    {% if user.is_authenticated %}
+        <span style='float: right;'>{{ user }}</span>
+    {% endif %}
+    <div class='clr' ></div>
+{% endblock %}
diff --git a/src/archive/templates/archive/file_managed.html b/src/archive/templates/archive/file_managed.html
new file mode 100755 (executable)
index 0000000..8917243
--- /dev/null
@@ -0,0 +1,135 @@
+{% extends "archive/base.html" %}
+{% load i18n %}
+{% load tags %}
+
+{% block content %}
+
+<p>Plik ÅºródÅ‚owy: <a href='{% url "download" audiobook.id %}'>{{ path }}</a>
+(sha1: <tt>{{ audiobook.source_sha1 }}</tt>).
+</p>
+
+<h2>{% trans "Publishing" %}</h2>
+
+{% if audiobook.mp3_status or audiobook.ogg_status %}
+
+<h2>{% trans "Publishing pending" %}</h2>
+
+<form method="post" action="{% url 'cancel_publishing' audiobook.id %}">
+    {% csrf_token %}
+    <input type="submit" value="{% trans "Cancel publishing" %}" />
+</form>
+
+
+{% if audiobook.mp3_status %}
+    <hr/>
+    <h2>MP3</h2>
+
+    {% tags_table audiobook.mp3_tags.tags %}
+
+    <p>Status: <b>{{ audiobook.get_mp3_status_display }}</b></p>
+{% endif %}
+
+{% if audiobook.ogg_status %}
+    <hr/>
+    <h2>Ogg Vorbis</h2>
+
+    {% tags_table audiobook.ogg_tags.tags %}
+
+    <p>Status: <b>{{ audiobook.get_ogg_status_display }}</b></p>
+{% endif %}
+
+
+
+
+{% else %}
+    <table class='tags'>
+        {% tags_table audiobook.new_publish_tags 0 %}
+        <tr><th></th><td>
+
+            <form method="post" action="{% url 'publish' audiobook.id %}">
+                {% csrf_token %}
+                <input type="submit" value="{% trans "Publish" %}" />
+            </form>
+
+            {% if not audiobook.mp3_published or not audiobook.ogg_published %}
+            <form method="post" action="{% url 'convert' audiobook.id %}">
+                {% csrf_token %}
+                <input type="submit" value="{% trans "Convert without publishing" %}" />
+            </form>
+            {% endif %}
+
+        </td></tr>
+    </table>
+{% endif %}
+
+<hr/>
+{% if audiobook.mp3_file %}
+    <h2>{% trans "MP3 file" %}</h2>
+    <p><a href="{% url 'download' audiobook.id 'mp3' %}">{% trans "Download MP3 file." %}</a></p>
+    {% if audiobook.mp3_published %}
+        <p>{% trans "Published:" %} {{ audiobook.mp3_published }}</a></p>
+        {% if audiobook.mp3_published_tags.tags %}
+            {% tags_table audiobook.mp3_published_tags.tags %}
+        {% endif %}
+    {% else %}
+        <p>{% trans "Not published yet." %}</p>
+    {% endif %}
+{% else %}
+    <p>{% trans "MP3 file hasn't been generated yet." %}</p>
+{% endif %}
+
+<hr/>
+{% if audiobook.ogg_file %}
+    <h2>{% trans "Ogg Vorbis file" %}</h2>
+    <p><a href="{% url 'download' audiobook.id 'ogg' %}">{% trans "Download Ogg Vorbis file." %}</a></p>
+    {% if audiobook.ogg_published %}
+        <p>{% trans "Published:" %} {{ audiobook.ogg_published }}</a></p>
+        {% if audiobook.ogg_published_tags.tags %}
+            {% tags_table audiobook.ogg_published_tags.tags %}
+        {% endif %}
+    {% else %}
+        <p>{% trans "Not published yet." %}</p>
+    {% endif %}
+{% else %}
+    <p>{% trans "Ogg Vorbis file hasn't been generated yet." %}</p>
+{% endif %}
+
+
+
+
+<hr />
+
+
+
+
+<h2>{% trans "Update tags" %}</h2>
+
+Last modified: {{ audiobook.modified }}
+
+{% multiple_tags_table tags %}
+
+
+
+<form method='post' action='.'>
+    {% csrf_token %}
+    <table>
+        {{ form.as_table }}
+        <td></td><td><input type="submit" value='{% trans "Commit" %}' /></td></td>
+    </table>
+</form>
+
+
+
+<hr />
+
+
+
+<form method="post" action="{% url 'remove_to_archive' audiobook.id %}"
+    onsubmit='return confirm("{% trans "Are you sure you want to move this audiobook to archive?" %}")'>
+    {% csrf_token %}
+    <input type="submit" value="{% trans "Remove to archive" %}" />
+</form>
+
+
+
+{% endblock %}
diff --git a/src/archive/templates/archive/file_new.html b/src/archive/templates/archive/file_new.html
new file mode 100644 (file)
index 0000000..acc161d
--- /dev/null
@@ -0,0 +1,23 @@
+{% extends "archive/base.html" %}
+{% load i18n tags %}
+
+{% block content %}
+
+<form method="post" action="{% url 'move_to_archive' filename %}">
+    {% csrf_token %}
+    <input type="submit" value="{% trans "Move to archive" %}" />
+</form>
+
+
+{% multiple_tags_table tags %}
+
+
+<form method='post' action='.'>
+    {% csrf_token %}
+    <table>
+        {{ form.as_table }}
+        <td></td><td><input type="submit" value='{% trans "Commit" %}' /></td></td>
+    </table>
+</form>
+
+{% endblock %}
diff --git a/src/archive/templates/archive/file_unmanaged.html b/src/archive/templates/archive/file_unmanaged.html
new file mode 100755 (executable)
index 0000000..89f79ba
--- /dev/null
@@ -0,0 +1,31 @@
+{% extends "archive/base.html" %}
+{% load i18n %}
+
+{% block messages %}
+    {% if err_exists %}
+        <p>{% trans "File with same name already exists!" %}</p>
+    {% endif %}
+{% endblock %}
+
+
+{% block content %}
+
+
+<ul id="tags">
+    {% for t, v in tags.items %}
+        <li>{{t}}
+        <ul>
+        {% for x in v %}
+            <li>{{x}}</li>
+        {% endfor %}
+        </ul></li>
+    {% endfor %}
+</ul>
+
+
+<form method="post" action="{% url 'move_to_new' filename %}">
+    {% csrf_token %}
+    <input type="submit" value="{% trans "Move to new files" %}" />
+</form>
+
+{% endblock %}
diff --git a/src/archive/templates/archive/list.html b/src/archive/templates/archive/list.html
new file mode 100644 (file)
index 0000000..bcd85de
--- /dev/null
@@ -0,0 +1,15 @@
+{% extends "archive/base.html" %}
+{% load i18n %}
+
+
+{% block content %}
+
+    <div id="file-list">
+        <h1>{% block file-list-title %}{% endblock %}</h1>
+        <div>{% block file-list-info %}{% endblock %}</div>
+        {% block file-list-wrapper %}
+            <ul>{% block file-list %}{% endblock %}</ul>
+        {% endblock file-list-wrapper %}
+    </div>
+
+{% endblock %}
diff --git a/src/archive/templates/archive/list_new.html b/src/archive/templates/archive/list_new.html
new file mode 100644 (file)
index 0000000..fabf446
--- /dev/null
@@ -0,0 +1,22 @@
+{% extends "archive/list.html" %}
+{% load i18n %}
+
+
+{% block file-list-title %}
+    {% trans "New audiobooks" %}
+{% endblock %}
+
+
+{% block file-list-info %}
+    {% trans "Put source audiobooks in:" %}
+    <span>{{ path }}</span>
+{% endblock %}
+
+
+{% block file-list %}
+    {% for file in objects %}
+        <li>
+            <a href='{% url "file_new" file|urlencode %}'>{{ file }}</a>
+        </li>
+    {% endfor %}
+{% endblock %}
diff --git a/src/archive/templates/archive/list_published.html b/src/archive/templates/archive/list_published.html
new file mode 100755 (executable)
index 0000000..5e61ba1
--- /dev/null
@@ -0,0 +1,20 @@
+{% extends "archive/list.html" %}
+{% load i18n %}
+
+
+{% block file-list-title %}
+    {% trans "Published audiobooks" %}
+{% endblock %}
+
+
+{% block file-list-info %}
+{% endblock %}
+
+
+{% block file-list %}
+    {% for file in objects %}
+        <li>
+            <a href='{% url "file" file.id %}'>{{ file }}</a>
+        </li>
+    {% endfor %}
+{% endblock %}
diff --git a/src/archive/templates/archive/list_publishing.html b/src/archive/templates/archive/list_publishing.html
new file mode 100755 (executable)
index 0000000..6bcf9e0
--- /dev/null
@@ -0,0 +1,23 @@
+{% extends "archive/list.html" %}
+{% load i18n %}
+
+
+{% block file-list-title %}
+    {% trans "Audiobooks being published" %}
+{% endblock %}
+
+
+
+{% block file-list-wrapper %}
+    {% for k, objects in status_objects %}
+        <h2>{{ k.1 }}</h2>
+        <ul>
+            {% for file in objects %}
+            <li>
+                <a href='{% url "file" file.id %}'>{{ file }}</a>
+                ({% if file.mp3_status = k.0 %}MP3{% if file.ogg_status = k.0 %}, {% endif %}{% endif %}{% if file.ogg_status = k.0 %}Ogg{% endif %})
+            </li>
+            {% endfor %}
+        </ul>
+    {% endfor %}
+{% endblock %}
diff --git a/src/archive/templates/archive/list_unmanaged.html b/src/archive/templates/archive/list_unmanaged.html
new file mode 100755 (executable)
index 0000000..fd48c33
--- /dev/null
@@ -0,0 +1,19 @@
+{% extends "archive/list.html" %}
+{% load i18n %}
+
+{% block file-list-title %}
+    {% trans "Unmanaged archive" %}
+{% endblock %}
+
+
+{% block file-list-info %}
+{% endblock %}
+
+
+{% block file-list %}
+    {% for file in objects %}
+        <li>
+            <a href='{% url "file_unmanaged" file|urlencode %}'>{{ file }}</a>
+        </li>
+    {% endfor %}
+{% endblock %}
diff --git a/src/archive/templates/archive/list_unpublished.html b/src/archive/templates/archive/list_unpublished.html
new file mode 100755 (executable)
index 0000000..b901ef0
--- /dev/null
@@ -0,0 +1,32 @@
+{% extends "archive/list.html" %}
+{% load i18n %}
+
+
+{% block file-list-title %}
+    {% trans "Unpublished audiobooks" %}
+{% endblock %}
+
+
+{% block file-list-info %}
+{% endblock %}
+
+
+{% block file-list %}
+    {% for file in objects %}
+        <li>
+            <a href='{% url "file" file.id %}'>{{ file }}</a>
+
+            {% if file.mp3_published %}
+                <span class="list-published-tag" title="{{ file.mp3_published }}">MP3</span>
+            {% else %}{% if file.mp3_status %}
+                <span class="list-publishing-tag" title="{{ file.get_mp3_status_display }}">MP3</span>
+            {% endif %}{% endif %}
+
+            {% if file.ogg_published %}
+                <span class="list-published-tag" title="{{ file.ogg_published }}">Ogg</span>
+            {% else %}{% if file.ogg_status %}
+                <span class="list-publishing-tag" title="{{ file.get_ogg_status_display }}">Ogg</span>
+            {% endif %}{% endif %}
+        </li>
+    {% endfor %}
+{% endblock %}
diff --git a/src/archive/templates/archive/tags/multiple_tags_table.html b/src/archive/templates/archive/tags/multiple_tags_table.html
new file mode 100755 (executable)
index 0000000..4609278
--- /dev/null
@@ -0,0 +1,9 @@
+{% if table %}<table class='multiple_tags'>{% endif %}
+    {% for t, v in tags.items %}
+        <tr><th>{{ t }}</th><td>
+        {% for x in v %}
+            <div>{{ x|linebreaksbr }}</div>
+        {% endfor %}
+        </td></tr>
+    {% endfor %}
+{% if table %}</table>{% endif %}
diff --git a/src/archive/templates/archive/tags/tags_table.html b/src/archive/templates/archive/tags/tags_table.html
new file mode 100755 (executable)
index 0000000..70b9d48
--- /dev/null
@@ -0,0 +1,7 @@
+{% if table %}<table class='tags'>{% endif %}
+    {% for t, v in tags.items %}
+        <tr><th>{{ t }}</th><td>
+            {{ v|linebreaksbr }}
+        </td></tr>
+    {% endfor %}
+{% if table %}</table>{% endif %}
diff --git a/src/archive/templates/base.html b/src/archive/templates/base.html
new file mode 100755 (executable)
index 0000000..5e0053f
--- /dev/null
@@ -0,0 +1,24 @@
+{% load i18n %}
+<!DOCTYPE html>
+<html>
+<head>
+    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+    <link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}style.css?20110808" />
+    <title>{% trans "Audiobook repository" %}</title>
+</head>
+<body>
+
+<div id="repo-zones-nav">
+    {% block repo-zones-nav %}&nbsp;{% endblock %}
+</div>
+
+<div id="messages">
+{% block messages %}{% endblock %}
+</div>
+
+<div id="content">
+{% block content %}{% endblock %}
+</div>
+
+</body>
+</html>
diff --git a/src/archive/templates/registration/login.html b/src/archive/templates/registration/login.html
new file mode 100755 (executable)
index 0000000..115cdf1
--- /dev/null
@@ -0,0 +1,12 @@
+{% extends "base.html" %}
+{% load i18n %}
+
+{% block content %}
+
+<form method='post'>
+{% csrf_token %}
+{{ form.as_p }}
+<p><input type='submit' value='{% trans "Login" %}' /></p>
+</form>
+
+{% endblock %}
diff --git a/src/archive/templatetags/__init__.py b/src/archive/templatetags/__init__.py
new file mode 100755 (executable)
index 0000000..e69de29
diff --git a/src/archive/templatetags/tags.py b/src/archive/templatetags/tags.py
new file mode 100755 (executable)
index 0000000..8e208a6
--- /dev/null
@@ -0,0 +1,21 @@
+from django import template
+
+register = template.Library()
+
+@register.inclusion_tag('archive/tags/multiple_tags_table.html')
+def multiple_tags_table(tags, table=True):
+    new_tags = {}
+    if tags:
+        for k, v in tags.items():
+            if isinstance(v, list):
+                new_tags[k] = v
+            else:
+                new_tags[k] = [v]
+    return {"tags": new_tags, "table": table}
+
+
+@register.inclusion_tag('archive/tags/tags_table.html')
+def tags_table(tags, table=True):
+    if tags is None:
+        tags = {}
+    return locals()
diff --git a/src/archive/urls.py b/src/archive/urls.py
new file mode 100644 (file)
index 0000000..efaeeea
--- /dev/null
@@ -0,0 +1,25 @@
+from django.conf.urls import patterns, include, url
+from django.views.generic import RedirectView
+
+urlpatterns = patterns('',
+    url(r'^$', RedirectView.as_view(url='new/')),
+
+    url(r'^new/$', 'archive.views.list_new', name="list_new"),
+    url(r'^new/(.+)/$', 'archive.views.file_new', name="file_new"),
+    url(r'^move_to_archive/(.+)/$', 'archive.views.move_to_archive', name="move_to_archive"),
+
+    url(r'^unpublished/$', 'archive.views.list_unpublished', name="list_unpublished"),
+    url(r'^publishing/$', 'archive.views.list_publishing', name="list_publishing"),
+    url(r'^published/$', 'archive.views.list_published', name="list_published"),
+    url(r'^file/(\d+)/$', 'archive.views.file_managed', name="file"),
+    url(r'^publish/(\d+)/$', 'archive.views.publish', name="publish"),
+    url(r'^convert/(\d+)/$', 'archive.views.publish', {'publish': False}, name="convert"),
+    url(r'^download/(\d+)/$', 'archive.views.download', name="download"),
+    url(r'^download/(\d+)\.(mp3|ogg)$', 'archive.views.download', name="download"),
+    url(r'^cancel/(\d+)/$', 'archive.views.cancel_publishing', name="cancel_publishing"),
+    url(r'^remove_to_archive/(\d+)/$', 'archive.views.remove_to_archive', name="remove_to_archive"),
+
+    url(r'^unmanaged/$', 'archive.views.list_unmanaged', name="list_unmanaged"),
+    url(r'^unmanaged/(.+)/$', 'archive.views.file_unmanaged', name="file_unmanaged"),
+    url(r'^move_to_new/(.+)/$', 'archive.views.move_to_new', name="move_to_new"),
+)
diff --git a/src/archive/utils.py b/src/archive/utils.py
new file mode 100644 (file)
index 0000000..3e89a8b
--- /dev/null
@@ -0,0 +1,44 @@
+from hashlib import sha1
+import os
+import os.path
+from django.core.files.storage import FileSystemStorage
+from django.core.files.uploadedfile import UploadedFile
+
+
+class ExistingFile(UploadedFile):
+
+    def __init__(self, path, *args, **kwargs):
+        self.path = path
+        return super(ExistingFile, self).__init__(*args, **kwargs)
+
+    def temporary_file_path(self):
+        return self.path
+
+    def close(self):
+        pass
+
+
+class OverwriteStorage(FileSystemStorage):
+
+    def _save(self, name, content):
+        if self.exists(name):
+            self.delete(name)
+        return super(OverwriteStorage, self)._save(name, content)
+
+    def get_available_name(self, name):
+        return name
+
+
+def sha1_file(f):
+    sha = sha1()
+    for piece in iter(lambda: f.read(1024*1024), ''):
+        sha.update(piece)
+    return sha.hexdigest()
+
+
+def all_files(root_path):
+    root_len = len(root_path)
+    for path, dirs, files in os.walk(root_path):
+       for fname in files:
+           yield os.path.join(path, fname)[root_len:].lstrip('/')
+
diff --git a/src/archive/views.py b/src/archive/views.py
new file mode 100644 (file)
index 0000000..b5850a0
--- /dev/null
@@ -0,0 +1,279 @@
+# Create your views here.
+
+from datetime import datetime
+import os
+import os.path
+from urllib import quote
+
+from archive import settings
+from django.contrib.auth import logout
+from django.contrib.auth.decorators import permission_required
+from django.core.urlresolvers import reverse
+from django.db.models import Q, Max
+from django.http import Http404, HttpResponse
+from django.shortcuts import render, redirect, get_object_or_404
+from django.views.decorators.http import require_POST
+
+import mutagen
+
+from archive.constants import status
+from archive import models
+from archive.forms import AudiobookForm
+from archive import tasks
+from archive.utils import all_files
+
+
+def list_new(request):
+    division = 'new'
+
+    path = settings.NEW_PATH
+    objects = sorted(all_files(path))
+    return render(request, "archive/list_new.html", locals())
+
+
+@permission_required('archive.change_audiobook')
+def file_new(request, filename):
+    division = 'new'
+
+    filepath = filename.encode('utf-8')
+    root_filepath = os.path.join(settings.NEW_PATH, filename.encode('utf-8'))
+    if request.POST:
+        form = AudiobookForm(request.POST)
+        if form.is_valid():
+            try:
+                form.save(path=filepath)
+            except IOError:
+                raise Http404
+            return redirect(list_new)
+
+    try:
+        tags = mutagen.File(root_filepath)
+    except IOError:
+        raise Http404
+    d = {}
+    if tags:
+        for tag in tags:
+            value = tags[tag]
+            if isinstance(value, list):
+                d[tag] = value[0]
+            else:
+                d[tag] = value
+            if tag == 'project':
+                try:
+                    d[tag] = models.Project.objects.get(name=d[tag]).pk
+                except models.Project.DoesNotExist:
+                    d[tag] = None
+
+    if not request.POST:
+        form = AudiobookForm(d)
+    return render(request, "archive/file_new.html", locals())
+
+
+@require_POST
+@permission_required('archive.change_audiobook')
+def move_to_archive(request, filename):
+    """ move a new file to the unmanaged files dir """
+
+    filename_str = filename.encode('utf-8')
+    old_path = os.path.join(settings.NEW_PATH, filename_str)
+    new_path = os.path.join(settings.UNMANAGED_PATH, filename_str)
+    new_dir = os.path.split(new_path)[0]
+    if not os.path.isdir(new_dir):
+        os.makedirs(new_dir)
+
+    if not os.path.isfile(old_path):
+        raise Http404
+
+    try:
+        os.link(old_path, new_path)
+        os.unlink(old_path)
+    except OSError:
+        # destination file exists, don't overwrite it
+        # TODO: this should probably be more informative
+        return redirect(file_new, filename)
+
+    return redirect(list_new)
+
+
+@require_POST
+@permission_required('archive.change_audiobook')
+def remove_to_archive(request, aid):
+    """ move a managed file to the unmanaged files dir """
+
+    audiobook = get_object_or_404(models.Audiobook, id=aid)
+    old_path = audiobook.source_file.path
+    new_path = os.path.join(settings.UNMANAGED_PATH,
+        str(audiobook.source_file)[len(settings.FILES_SAVE_PATH):].lstrip('/'))
+    new_dir = os.path.split(new_path)[0]
+    if not os.path.isdir(new_dir):
+        os.makedirs(new_dir)
+
+    if not os.path.isfile(old_path):
+        raise Http404
+
+    success = False
+    try_new_path = new_path
+    try_number = 0
+    while not success:
+        try:
+            os.link(old_path, try_new_path)
+        except OSError:
+            # destination file exists, don't overwrite it
+            try_number += 1
+            parts = new_path.rsplit('.', 1)
+            parts[0] += '_%d' % try_number
+            try_new_path = ".".join(parts)
+        else:
+            os.unlink(old_path)
+            audiobook.delete()
+            success = True
+
+    return redirect(list_unmanaged)
+
+@require_POST
+@permission_required('archive.change_audiobook')
+def move_to_new(request, filename):
+    """ move a unmanaged file to new files dir """
+
+    filename_str = filename.encode('utf-8')
+    old_path = os.path.join(settings.UNMANAGED_PATH, filename_str)
+    new_path = os.path.join(settings.NEW_PATH, filename_str)
+    new_dir = os.path.split(new_path)[0]
+    if not os.path.isdir(new_dir):
+        os.makedirs(new_dir)
+
+    if not os.path.isfile(old_path):
+        raise Http404
+
+    try:
+        os.link(old_path, new_path)
+        os.unlink(old_path)
+    except OSError:
+        # destination file exists, don't overwrite it
+        # TODO: this should probably be more informative
+        return redirect(reverse(file_unmanaged, args=[filename]) + "?exists=1")
+
+    return redirect(list_unmanaged)
+
+
+@require_POST
+@permission_required('archive.change_audiobook')
+def publish(request, aid, publish=True):
+    """ mark file for publishing """
+    audiobook = get_object_or_404(models.Audiobook, id=aid)
+    tags = {
+        'name': audiobook.title,
+        'url': audiobook.url,
+        'tags': audiobook.new_publish_tags(),
+        }
+    audiobook.mp3_tags = tags
+    audiobook.ogg_tags = tags
+    audiobook.mp3_status = audiobook.ogg_status = status.WAITING
+    audiobook.save()
+    # isn't there a race here?
+    audiobook.mp3_task = tasks.Mp3Task.delay(aid, publish).task_id
+    audiobook.ogg_task = tasks.OggTask.delay(aid, publish).task_id
+    audiobook.save()
+
+    return redirect(file_managed, aid)
+
+
+@require_POST
+@permission_required('archive.change_audiobook')
+def cancel_publishing(request, aid):
+    """ cancel scheduled publishing """
+    audiobook = get_object_or_404(models.Audiobook, id=aid)
+    # TODO: cancel tasks
+    audiobook.mp3_status = None
+    audiobook.ogg_status = None
+    audiobook.save()
+    return redirect(file_managed, aid)
+
+
+def download(request, aid, which="source"):
+    if which not in ("source", "mp3", "ogg"):
+        raise Http404
+    audiobook = get_object_or_404(models.Audiobook, id=aid)
+    file_ = getattr(audiobook, "%s_file" % which)
+    if not file_:
+        raise Http404
+    ext = file_.path.rsplit('.', 1)[-1]
+    response = HttpResponse(mimetype='application/force-download')
+    
+    response['Content-Disposition'] = "attachment; filename*=UTF-8''%s.%s" % (
+        quote(audiobook.title.encode('utf-8'), safe=''), ext)
+    response['X-Sendfile'] = file_.path.encode('utf-8')
+    return response
+
+
+def list_unpublished(request):
+    division = 'unpublished'
+
+    objects = models.Audiobook.objects.filter(Q(mp3_published=None) | Q(ogg_published=None))
+    return render(request, "archive/list_unpublished.html", locals())
+
+
+def list_publishing(request):
+    division = 'publishing'
+
+    objects = models.Audiobook.objects.exclude(mp3_status=None, ogg_status=None)
+    objects_by_status = {}
+    for o in objects:
+        if o.mp3_status:
+            k = o.mp3_status, o.get_mp3_status_display()
+            objects_by_status.setdefault(k, []).append(o)
+        if o.ogg_status and o.ogg_status != o.mp3_status:
+            k = o.ogg_status, o.get_ogg_status_display()
+            objects_by_status.setdefault(k, []).append(o)
+    status_objects = sorted(objects_by_status.items(), reverse=True)
+
+    return render(request, "archive/list_publishing.html", locals())
+
+
+def list_published(request):
+    division = 'published'
+
+    objects = models.Audiobook.objects.exclude(Q(mp3_published=None) | Q(ogg_published=None))
+    return render(request, "archive/list_published.html", locals())
+
+
+@permission_required('archive.change_audiobook')
+def file_managed(request, id):
+    audiobook = get_object_or_404(models.Audiobook, id=id)
+
+    if request.POST:
+        form = AudiobookForm(request.POST, instance=audiobook)
+        if form.is_valid():
+            try:
+                form.save()
+            except IOError:
+                raise Http404
+
+    division = 'published' if audiobook.published() else 'unpublished'
+    path = audiobook.source_file.path[len(settings.FILES_PATH):].lstrip('/')
+
+    # for tags update
+    tags = mutagen.File(audiobook.source_file.path.encode('utf-8'))
+    if not tags:
+        tags = {}
+    form = AudiobookForm(instance=audiobook)
+
+    return render(request, "archive/file_managed.html", locals())
+
+
+def list_unmanaged(request):
+    division = 'unmanaged'
+
+    objects = sorted(all_files(settings.UNMANAGED_PATH))
+    return render(request, "archive/list_unmanaged.html", locals())
+
+
+def file_unmanaged(request, filename):
+    division = 'unmanaged'
+
+    tags = mutagen.File(os.path.join(settings.UNMANAGED_PATH, filename.encode('utf-8')))
+    if not tags:
+        tags = {}
+    
+    err_exists = request.GET.get('exists')
+    return render(request, "archive/file_unmanaged.html", locals())
diff --git a/src/audiobooks/__init__.py b/src/audiobooks/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/audiobooks/settings.py b/src/audiobooks/settings.py
new file mode 100644 (file)
index 0000000..6ec4fe7
--- /dev/null
@@ -0,0 +1,196 @@
+# Django settings for audiobooks project.
+# -*- coding: utf-8 -*-
+
+import os
+PROJECT_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
+
+
+
+DEBUG = False
+TEMPLATE_DEBUG = DEBUG
+
+ADMINS = (
+    # ('Your Name', 'your_email@example.com'),
+)
+
+MANAGERS = ADMINS
+
+DATABASES = {
+    'default': {
+        'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
+        'NAME': os.path.join(PROJECT_ROOT, 'dev.sqlite'),                      # Or path to database file if using sqlite3.
+        'USER': '',                      # Not used with sqlite3.
+        'PASSWORD': '',                  # Not used with sqlite3.
+        'HOST': '',                      # Set to empty string for localhost. Not used with sqlite3.
+        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
+    }
+}
+
+# Local time zone for this installation. Choices can be found here:
+# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
+# although not all choices may be available on all operating systems.
+# On Unix systems, a value of None will cause Django to use the same
+# timezone as the operating system.
+# If running in a Windows environment this must be set to the same as your
+# system time zone.
+TIME_ZONE = 'Europe/Warsaw'
+
+# Language code for this installation. All choices can be found here:
+# http://www.i18nguy.com/unicode/language-identifiers.html
+LANGUAGE_CODE = 'pl'
+
+SITE_ID = 1
+
+# If you set this to False, Django will make some optimizations so as not
+# to load the internationalization machinery.
+USE_I18N = True
+
+# If you set this to False, Django will not format dates, numbers and
+# calendars according to the current locale
+USE_L10N = True
+
+# Absolute filesystem path to the directory that will hold user-uploaded files.
+# Example: "/home/media/media.lawrence.com/media/"
+MEDIA_ROOT = os.path.join(PROJECT_ROOT, '../media/')
+
+# URL that handles the media served from MEDIA_ROOT. Make sure to use a
+# trailing slash.
+# Examples: "http://media.lawrence.com/media/", "http://example.com/media/"
+MEDIA_URL = '/media/'
+
+# Absolute path to the directory static files should be collected to.
+# Don't put anything in this directory yourself; store your static files
+# in apps' "static/" subdirectories and in STATICFILES_DIRS.
+# Example: "/home/media/media.lawrence.com/static/"
+STATIC_ROOT = ''
+
+# URL prefix for static files.
+# Example: "http://media.lawrence.com/static/"
+STATIC_URL = '/static/'
+
+# URL prefix for admin static files -- CSS, JavaScript and images.
+# Make sure to use a trailing slash.
+# Examples: "http://foo.com/static/admin/", "/static/admin/".
+ADMIN_MEDIA_PREFIX = '/static/admin/'
+
+# Additional locations of static files
+STATICFILES_DIRS = (
+    # Put strings here, like "/home/html/static" or "C:/www/django/static".
+    # Always use forward slashes, even on Windows.
+    # Don't forget to use absolute paths, not relative paths.
+)
+
+# List of finder classes that know how to find static files in
+# various locations.
+STATICFILES_FINDERS = (
+    'django.contrib.staticfiles.finders.FileSystemFinder',
+    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
+#    'django.contrib.staticfiles.finders.DefaultStorageFinder',
+)
+
+# Make this unique, and don't share it with anybody.
+SECRET_KEY = '8ehk^-+pr(o)k6lh_gl9+ks6gkd9u#fka7fv%ikpk(c%llqa6%'
+
+# List of callables that know how to import templates from various sources.
+TEMPLATE_LOADERS = (
+    'django.template.loaders.filesystem.Loader',
+    'django.template.loaders.app_directories.Loader',
+#     'django.template.loaders.eggs.Loader',
+)
+
+MIDDLEWARE_CLASSES = (
+    'django.middleware.common.CommonMiddleware',
+    'django.contrib.sessions.middleware.SessionMiddleware',
+    'django.middleware.csrf.CsrfViewMiddleware',
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+    'django_cas.middleware.CASMiddleware',
+    'django.contrib.messages.middleware.MessageMiddleware',
+)
+
+AUTHENTICATION_BACKENDS = (
+    'django.contrib.auth.backends.ModelBackend',
+    'django_cas.backends.CASBackend',
+)
+
+ROOT_URLCONF = 'audiobooks.urls'
+
+TEMPLATE_DIRS = (
+    # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
+    # Always use forward slashes, even on Windows.
+    # Don't forget to use absolute paths, not relative paths.
+)
+
+INSTALLED_APPS = (
+    'django.contrib.auth',
+    'django.contrib.contenttypes',
+    'django.contrib.sessions',
+    'django.contrib.sites',
+    'django.contrib.messages',
+    'django.contrib.staticfiles',
+    # Uncomment the next line to enable the admin:
+    'django.contrib.admin',
+    # Uncomment the next line to enable admin documentation:
+    # 'django.contrib.admindocs',
+
+    'djcelery',
+    'djkombu',
+
+    'south',
+    'archive',
+)
+
+# A sample logging configuration. The only tangible logging
+# performed by this configuration is to send an email to
+# the site admins on every HTTP 500 error.
+# See http://docs.djangoproject.com/en/dev/topics/logging for
+# more details on how to customize your logging configuration.
+LOGGING = {
+    'version': 1,
+    'disable_existing_loggers': False,
+    'filters': {
+         'require_debug_false': {
+             '()': 'django.utils.log.RequireDebugFalse'
+         }
+     },
+    'handlers': {
+        'mail_admins': {
+            'level': 'ERROR',
+            'filters': ['require_debug_false'],
+            'class': 'django.utils.log.AdminEmailHandler'
+        }
+    },
+    'loggers': {
+        'django.request': {
+            'handlers': ['mail_admins'],
+            'level': 'ERROR',
+            'propagate': True,
+        },
+    }
+}
+
+#http://logowanie.nowoczesnapolska.org.pl/cas/'
+CAS_SERVER_URL = "http://logowanie.nowoczesnapolska.org.pl/cas/"
+CAS_VERSION = "1"
+
+
+EMAIL_SUBJECT_PREFIX = '[Audio] '
+SERVER_EMAIL = 'no-reply@audio.wolnelektury.pl'
+EMAIL_HOST = 'localhost'
+EMAIL_PORT = 25
+
+import djcelery
+djcelery.setup_loader()
+
+CELERY_SEND_TASK_ERROR_EMAILS = True
+BROKER_BACKEND = "djkombu.transport.DatabaseTransport"
+BROKER_HOST = "localhost"
+BROKER_PORT = 5672
+BROKER_USER = "guest"
+BROKER_PASSWORD = "guest"
+BROKER_VHOST = "/"
+
+
+try:
+    from localsettings import *
+except:
+    pass
diff --git a/src/audiobooks/urls.py b/src/audiobooks/urls.py
new file mode 100644 (file)
index 0000000..f3113c8
--- /dev/null
@@ -0,0 +1,21 @@
+from django.conf import settings
+from django.conf.urls import patterns, include, url
+from django.views.generic import RedirectView
+
+# Uncomment the next two lines to enable the admin:
+from django.contrib import admin
+admin.autodiscover()
+
+urlpatterns = patterns('',
+    url(r'^$', RedirectView.as_view(url='archive/')),
+    url(r'^archive/', include('archive.urls')),
+
+    url(r'^admin/', include(admin.site.urls)),
+    url(r'^accounts/login/$', 'django_cas.views.login', name='login'),
+    url(r'^accounts/logout/$', 'django_cas.views.logout', name='logout'),
+)
+
+if settings.DEBUG:
+    urlpatterns += patterns('',
+        (r'^media/(?P<path>.*)$', 'django.views.static.serve', {'document_root': settings.MEDIA_ROOT, 'show_indexes':True}),
+)
diff --git a/src/audiobooks/wsgi.py b/src/audiobooks/wsgi.py
new file mode 100644 (file)
index 0000000..5e0ddaa
--- /dev/null
@@ -0,0 +1,28 @@
+"""
+WSGI config for edumed project.
+
+This module contains the WSGI application used by Django's development server
+and any production WSGI deployments. It should expose a module-level variable
+named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover
+this application via the ``WSGI_APPLICATION`` setting.
+
+Usually you will have the standard Django WSGI application here, but it also
+might make sense to replace the whole Django WSGI application with a custom one
+that later delegates to the Django one. For example, you could introduce WSGI
+middleware here, or combine a Django application with an application of another
+framework.
+
+"""
+import os
+
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "audiobooks.settings")
+
+# This application object is used by any WSGI server configured to use this
+# file. This includes Django's development server, if the WSGI_APPLICATION
+# setting points here.
+from django.core.wsgi import get_wsgi_application
+application = get_wsgi_application()
+
+# Apply WSGI middleware here.
+# from helloworld.wsgi import HelloWorldApplication
+# application = HelloWorldApplication(application)
diff --git a/src/manage.py b/src/manage.py
new file mode 100755 (executable)
index 0000000..3281d70
--- /dev/null
@@ -0,0 +1,14 @@
+#!/usr/bin/env python
+from django.core.management import execute_manager
+import imp
+try:
+    imp.find_module('settings', ['audiobooks'])
+except ImportError:
+    import sys
+    sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n" % __file__)
+    sys.exit(1)
+
+import audiobooks.settings
+
+if __name__ == "__main__":
+    execute_manager(audiobooks.settings)