+++ /dev/null
-from archive.models import Project, Audiobook
-from django.contrib import admin
-
-admin.site.register(Project)
-admin.site.register(Audiobook)
+++ /dev/null
-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')),
- ]
+++ /dev/null
-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()
+++ /dev/null
-# 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."
+++ /dev/null
-# 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']
+++ /dev/null
-# 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']
+++ /dev/null
-# 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']
+++ /dev/null
-# 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']
+++ /dev/null
-# -*- 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
+++ /dev/null
-# -*- 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
-
+++ /dev/null
-# -*- 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')
-
+++ /dev/null
-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;
-}
+++ /dev/null
-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')
- ])
+++ /dev/null
-{% extends "archive/base.html" %}
-{% block content %}
-<h1>Nie znaleziono strony.</h1>
-
-<p>Nie znaleziono żądanej strony.</p>
-{% endblock %}
+++ /dev/null
-BÅ‚Ä…d serwera.
+++ /dev/null
-{% 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 %}
+++ /dev/null
-{% 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 %}
+++ /dev/null
-{% 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 %}
+++ /dev/null
-{% 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 %}
+++ /dev/null
-{% 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 %}
+++ /dev/null
-{% 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 %}
+++ /dev/null
-{% 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 %}
+++ /dev/null
-{% 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 %}
+++ /dev/null
-{% 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 %}
+++ /dev/null
-{% 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 %}
+++ /dev/null
-{% 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 %}
+++ /dev/null
-{% 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 %}
+++ /dev/null
-{% 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 %} {% endblock %}
-</div>
-
-<div id="messages">
-{% block messages %}{% endblock %}
-</div>
-
-<div id="content">
-{% block content %}{% endblock %}
-</div>
-
-</body>
-</html>
+++ /dev/null
-{% 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 %}
+++ /dev/null
-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()
+++ /dev/null
-"""
-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)
+++ /dev/null
-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"),
-)
+++ /dev/null
-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('/')
-
+++ /dev/null
-# 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())
+++ /dev/null
-#!/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)
+++ /dev/null
-# 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
+++ /dev/null
-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}),
-)
+++ /dev/null
-"""
-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)
--- /dev/null
+from archive.models import Project, Audiobook
+from django.contrib import admin
+
+admin.site.register(Project)
+admin.site.register(Audiobook)
--- /dev/null
+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')),
+ ]
--- /dev/null
+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()
--- /dev/null
+# 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."
--- /dev/null
+# 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']
--- /dev/null
+# 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']
--- /dev/null
+# 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']
--- /dev/null
+# 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']
--- /dev/null
+# -*- 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
--- /dev/null
+# -*- 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
+
--- /dev/null
+# -*- 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')
+
--- /dev/null
+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;
+}
--- /dev/null
+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')
+ ])
--- /dev/null
+{% extends "archive/base.html" %}
+{% block content %}
+<h1>Nie znaleziono strony.</h1>
+
+<p>Nie znaleziono żądanej strony.</p>
+{% endblock %}
--- /dev/null
+BÅ‚Ä…d serwera.
--- /dev/null
+{% 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 %}
--- /dev/null
+{% 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 %}
--- /dev/null
+{% 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 %}
--- /dev/null
+{% 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 %}
--- /dev/null
+{% 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 %}
--- /dev/null
+{% 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 %}
--- /dev/null
+{% 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 %}
--- /dev/null
+{% 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 %}
--- /dev/null
+{% 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 %}
--- /dev/null
+{% 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 %}
--- /dev/null
+{% 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 %}
--- /dev/null
+{% 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 %}
--- /dev/null
+{% 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 %} {% endblock %}
+</div>
+
+<div id="messages">
+{% block messages %}{% endblock %}
+</div>
+
+<div id="content">
+{% block content %}{% endblock %}
+</div>
+
+</body>
+</html>
--- /dev/null
+{% 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 %}
--- /dev/null
+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()
--- /dev/null
+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"),
+)
--- /dev/null
+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('/')
+
--- /dev/null
+# 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())
--- /dev/null
+# 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
--- /dev/null
+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}),
+)
--- /dev/null
+"""
+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)
--- /dev/null
+#!/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)