From: Radek Czajka Date: Thu, 9 Jun 2011 08:51:14 +0000 (+0200) Subject: some moving works X-Git-Url: https://git.mdrn.pl/audio.git/commitdiff_plain/2d1d955bb3b4a5ca4dad1153c062c9ab3b6319e7 some moving works --- 2d1d955bb3b4a5ca4dad1153c062c9ab3b6319e7 diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..f1e760b --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.pyc +*.sqlite +localsettings.py diff --git a/apps/archive/__init__.py b/apps/archive/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/archive/admin.py b/apps/archive/admin.py new file mode 100644 index 0000000..d0804be --- /dev/null +++ b/apps/archive/admin.py @@ -0,0 +1,5 @@ +from archive.models import Project, Audiobook +from django.contrib import admin + +admin.site.register(Project) +admin.site.register(Audiobook) diff --git a/apps/archive/forms.py b/apps/archive/forms.py new file mode 100755 index 0000000..772ec0a --- /dev/null +++ b/apps/archive/forms.py @@ -0,0 +1,30 @@ +import os +import os.path +from datetime import datetime + +from django import forms +from django.utils.translation import ugettext_lazy as _ +import mutagen + +from archive.models import Audiobook +from archive.settings import FILES_PATH +from archive.utils import ExistingFile + +class AudiobookForm(forms.ModelForm): + class Meta: + model = Audiobook + + def save(self, commit=True, path=None): + 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 + m.source_file.save(os.path.basename(path), ExistingFile(path)) + + if commit: + m.save() + diff --git a/apps/archive/locale/pl/LC_MESSAGES/django.mo b/apps/archive/locale/pl/LC_MESSAGES/django.mo new file mode 100644 index 0000000..638fe28 Binary files /dev/null and b/apps/archive/locale/pl/LC_MESSAGES/django.mo differ diff --git a/apps/archive/locale/pl/LC_MESSAGES/django.po b/apps/archive/locale/pl/LC_MESSAGES/django.po new file mode 100644 index 0000000..a4a6ca4 --- /dev/null +++ b/apps/archive/locale/pl/LC_MESSAGES/django.po @@ -0,0 +1,145 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2011 Fundacja Nowoczesna Polska +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2011-05-17 12:35+0200\n" +"PO-Revision-Date: 2011-05-17 12:37+0100\n" +"Last-Translator: Radek Czajka \n" +"Language-Team: LANGUAGE \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" + +#: models.py:15 +#: models.py:32 +msgid "project" +msgstr "projekt" + +#: models.py:16 +msgid "projects" +msgstr "projekty" + +#: models.py:24 +msgid "source file" +msgstr "plik źródłowy" + +#: models.py:26 +msgid "title" +msgstr "tytuł" + +#: models.py:27 +msgid "artist" +msgstr "lektor" + +#: models.py:28 +msgid "arranger" +msgstr "aranżer" + +#: models.py:29 +msgid "encoded by" +msgstr "przyg. techn." + +#: models.py:30 +msgid "date" +msgstr "data" + +#: models.py:33 +msgid "book url" +msgstr "URL książki" + +#: models.py:46 +msgid "audiobook" +msgstr "audiobook" + +#: models.py:47 +msgid "audiobooks" +msgstr "audiobooki" + +#: templates/archive/base.html:7 +msgid "Audiobook repository" +msgstr "Repozytorium audiobooków" + +#: templates/archive/base.html:12 +msgid "New" +msgstr "Nowe" + +#: templates/archive/base.html:13 +msgid "Unpublished" +msgstr "Nie opublikowane" + +#: templates/archive/base.html:14 +msgid "Published" +msgstr "Opublikowane" + +#: templates/archive/base.html:15 +msgid "Archive" +msgstr "Archiwum" + +#: templates/archive/file_managed.html:6 +msgid "Publishing" +msgstr "Publikacja" + +#: templates/archive/file_managed.html:9 +msgid "Audiobook marked for publishing with tags:" +msgstr "Audiobook zaznaczony do publikacji z tagami:" + +#: templates/archive/file_managed.html:18 +msgid "Publishing already in progress." +msgstr "Publikowanie rozpoczęte." + +#: templates/archive/file_managed.html:22 +msgid "Cancel publishing" +msgstr "Anuluj publikację" + +#: templates/archive/file_managed.html:37 +msgid "Publish" +msgstr "Opublikuj" + +#: templates/archive/file_managed.html:50 +msgid "Update tags" +msgstr "Uaktualnij tagi" + +#: templates/archive/file_managed.html:69 +#: templates/archive/file_new.html:19 +msgid "Commit" +msgstr "Zatwierdź" + +#: 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_unmanaged.html:5 +msgid "Unmanaged archive" +msgstr "Audiobooki archiwalne" + +#: templates/archive/list_unpublished.html:6 +msgid "Unpublished audiobooks" +msgstr "Nie opublikowane audiobooki" + diff --git a/apps/archive/migrations/0001_initial.py b/apps/archive/migrations/0001_initial.py new file mode 100644 index 0000000..ac5a2c9 --- /dev/null +++ b/apps/archive/migrations/0001_initial.py @@ -0,0 +1,80 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Adding model 'Project' + db.create_table('archive_project', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=128, db_index=True)), + ('sponsors', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), + )) + db.send_create_signal('archive', ['Project']) + + # Adding model 'Audiobook' + db.create_table('archive_audiobook', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('source_file', self.gf('django.db.models.fields.files.FileField')(max_length=100)), + ('title', self.gf('django.db.models.fields.CharField')(max_length=255)), + ('artist', self.gf('django.db.models.fields.CharField')(max_length=255)), + ('conductor', self.gf('django.db.models.fields.CharField')(max_length=255)), + ('encoded_by', self.gf('django.db.models.fields.CharField')(max_length=255)), + ('date', self.gf('django.db.models.fields.CharField')(max_length=255)), + ('project', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['archive.Project'])), + ('url', self.gf('django.db.models.fields.URLField')(max_length=255)), + ('modified', self.gf('django.db.models.fields.DateTimeField')(null=True)), + ('published_tags', self.gf('jsonfield.fields.JSONField')(null=True)), + ('mp3_file', self.gf('django.db.models.fields.files.FileField')(max_length=100, null=True)), + ('ogg_file', self.gf('django.db.models.fields.files.FileField')(max_length=100, null=True)), + ('publishing_tags', self.gf('jsonfield.fields.JSONField')(null=True)), + ('publish_wait', self.gf('django.db.models.fields.DateTimeField')(null=True)), + ('publishing', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('published', self.gf('django.db.models.fields.DateTimeField')(null=True)), + )) + db.send_create_signal('archive', ['Audiobook']) + + + def backwards(self, orm): + + # Deleting model 'Project' + db.delete_table('archive_project') + + # Deleting model 'Audiobook' + db.delete_table('archive_audiobook') + + + models = { + 'archive.audiobook': { + 'Meta': {'ordering': "('title',)", 'object_name': 'Audiobook'}, + 'artist': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'conductor': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'date': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'encoded_by': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'mp3_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True'}), + 'ogg_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['archive.Project']"}), + 'publish_wait': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'published': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'published_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}), + 'publishing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'publishing_tags': ('jsonfield.fields.JSONField', [], {'null': 'True'}), + 'source_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'url': ('django.db.models.fields.URLField', [], {'max_length': '255'}) + }, + 'archive.project': { + 'Meta': {'ordering': "('name',)", 'object_name': 'Project'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'}), + 'sponsors': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) + } + } + + complete_apps = ['archive'] diff --git a/apps/archive/migrations/__init__.py b/apps/archive/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/archive/models.py b/apps/archive/models.py new file mode 100644 index 0000000..81a8594 --- /dev/null +++ b/apps/archive/models.py @@ -0,0 +1,56 @@ +from django.db import models +from jsonfield.fields import JSONField +from django.utils.translation import ugettext_lazy as _ + +# 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 + + +class Audiobook(models.Model): + source_file = models.FileField(upload_to='archive/files', verbose_name=_('source file'), editable=False) + + title = models.CharField(max_length=255, verbose_name=_('title')) + 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')) + modified = models.DateTimeField(null=True, editable=False) + + published_tags = JSONField(null=True, editable=False) + mp3_file = models.FileField(null=True, upload_to='archive/final', editable=False) + ogg_file = models.FileField(null=True, upload_to='archive/final', editable=False) + publishing_tags = JSONField(null=True, editable=False) + + publish_wait = models.DateTimeField(null=True, editable=False) # somebody hit "publish" + publishing = models.BooleanField(default=False, editable=False) + 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 new_publish_tags(self): + return { + 'title': self.title, + 'copyright': 'Fundacja Nowoczesna Polska', + } diff --git a/apps/archive/settings.py b/apps/archive/settings.py new file mode 100755 index 0000000..ea42c15 --- /dev/null +++ b/apps/archive/settings.py @@ -0,0 +1,32 @@ +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_FILES_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")) + + +# 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")) diff --git a/apps/archive/static/style.css b/apps/archive/static/style.css new file mode 100755 index 0000000..0d30ad6 --- /dev/null +++ b/apps/archive/static/style.css @@ -0,0 +1,49 @@ +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 { + 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; +} diff --git a/apps/archive/templates/archive/base.html b/apps/archive/templates/archive/base.html new file mode 100644 index 0000000..807227b --- /dev/null +++ b/apps/archive/templates/archive/base.html @@ -0,0 +1,28 @@ +{% load i18n %} + + + + + + {% trans "Audiobook repository" %} + + + + + +
+{% block messages %}{% endblock %} +
+ +
+{% block content %}{% endblock %} +
+ + + diff --git a/apps/archive/templates/archive/file_managed.html b/apps/archive/templates/archive/file_managed.html new file mode 100755 index 0000000..897aae2 --- /dev/null +++ b/apps/archive/templates/archive/file_managed.html @@ -0,0 +1,76 @@ +{% extends "archive/base.html" %} +{% load i18n %} + +{% block content %} + +

{% trans "Publishing" %}

+ +{% if audiobook.publish_wait %} +

{% trans "Audiobook marked for publishing with tags:" %}

+ + + {% for t, v in audiobook.publishing_tags.items %} + + {% endfor %} +
{{ t }}{{ v }}
+ + {% if audiobook.publishing %} +

{% trans "Publishing already in progress." %}

+ {% else %} +
+ {% csrf_token %} + +
+ {% endif %} +{% else %} + {% if audiobook.published %} + Here be currently published version, for comparison. + {% endif %} + +
+ + {% for k, v in audiobook.new_publish_tags.items %} + + {% endfor %} + + {% csrf_token %} + +
{{ k }}{{ v }}
+
+{% endif %} + + + + +
+ + + + +

{% trans "Update tags" %}

+ + + + {% for t, v in tags.items %} + + {% endfor %} +
{{t}} + {% for x in v %} + {{x}}
+ {% endfor %} +
+ + + +
+ {% csrf_token %} + + {{ form.as_table }} + +
+
+ + + + +{% endblock %} diff --git a/apps/archive/templates/archive/file_new.html b/apps/archive/templates/archive/file_new.html new file mode 100644 index 0000000..466c576 --- /dev/null +++ b/apps/archive/templates/archive/file_new.html @@ -0,0 +1,23 @@ +{% extends "archive/base.html" %} +{% load i18n tags %} + +{% block content %} + +
+ {% csrf_token %} + +
+ + +{% multiple_tags_table tags %} + + +
+ {% csrf_token %} + + {{ form.as_table }} + +
+
+ +{% endblock %} diff --git a/apps/archive/templates/archive/file_unmanaged.html b/apps/archive/templates/archive/file_unmanaged.html new file mode 100755 index 0000000..addd28e --- /dev/null +++ b/apps/archive/templates/archive/file_unmanaged.html @@ -0,0 +1,31 @@ +{% extends "archive/base.html" %} +{% load i18n %} + +{% block messages %} + {% if err_exists %} +

{% trans "File with same name already exists!" %}

+ {% endif %} +{% endblock %} + + +{% block content %} + + +
    + {% for t, v in tags.items %} +
  • {{t}} +
      + {% for x in v %} +
    • {{x}}
    • + {% endfor %} +
  • + {% endfor %} +
+ + +
+ {% csrf_token %} + +
+ +{% endblock %} diff --git a/apps/archive/templates/archive/list.html b/apps/archive/templates/archive/list.html new file mode 100644 index 0000000..9e3d85a --- /dev/null +++ b/apps/archive/templates/archive/list.html @@ -0,0 +1,13 @@ +{% extends "archive/base.html" %} +{% load i18n %} + + +{% block content %} + +
+

{% block file-list-title %}{% endblock %}

+
{% block file-list-info %}{% endblock %}
+
    {% block file-list %}{% endblock %}
+
+ +{% endblock %} diff --git a/apps/archive/templates/archive/list_new.html b/apps/archive/templates/archive/list_new.html new file mode 100644 index 0000000..c837cfd --- /dev/null +++ b/apps/archive/templates/archive/list_new.html @@ -0,0 +1,22 @@ +{% extends "archive/list.html" %} +{% load i18n %} + + +{% block file-list-title %} + {% trans "New audiobooks" %} +{% endblock %} + + +{% block file-list-info %} + {% trans "Put source audiobooks in:" %} + {{ path }} +{% endblock %} + + +{% block file-list %} + {% for file in objects %} +
  • + {{ file }} +
  • + {% endfor %} +{% endblock %} diff --git a/apps/archive/templates/archive/list_published.html b/apps/archive/templates/archive/list_published.html new file mode 100755 index 0000000..c51cace --- /dev/null +++ b/apps/archive/templates/archive/list_published.html @@ -0,0 +1,20 @@ +{% extends "archive/list.html" %} +{% load i18n %} + + +{% block file-list-title %} + {% trans "Published audiobooks" %} +{% endblock %} + + +{% block file-list-info %} +{% endblock %} + + +{% block file-list %} + {% for file in objects %} +
  • + {{ file }} +
  • + {% endfor %} +{% endblock %} diff --git a/apps/archive/templates/archive/list_unmanaged.html b/apps/archive/templates/archive/list_unmanaged.html new file mode 100755 index 0000000..96e45d3 --- /dev/null +++ b/apps/archive/templates/archive/list_unmanaged.html @@ -0,0 +1,19 @@ +{% extends "archive/list.html" %} +{% load i18n %} + +{% block file-list-title %} + {% trans "Unmanaged archive" %} +{% endblock %} + + +{% block file-list-info %} +{% endblock %} + + +{% block file-list %} + {% for file in objects %} +
  • + {{ file }} +
  • + {% endfor %} +{% endblock %} diff --git a/apps/archive/templates/archive/list_unpublished.html b/apps/archive/templates/archive/list_unpublished.html new file mode 100755 index 0000000..466072c --- /dev/null +++ b/apps/archive/templates/archive/list_unpublished.html @@ -0,0 +1,20 @@ +{% 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 %} +
  • + {{ file }} +
  • + {% endfor %} +{% endblock %} diff --git a/apps/archive/templates/archive/tags/multiple_tags_table.html b/apps/archive/templates/archive/tags/multiple_tags_table.html new file mode 100755 index 0000000..4a39808 --- /dev/null +++ b/apps/archive/templates/archive/tags/multiple_tags_table.html @@ -0,0 +1,9 @@ + + {% for t, v in tags.items %} + + {% endfor %} +
    {{t}} + {% for x in v %} +
    {{x}}
    + {% endfor %} +
    diff --git a/apps/archive/templatetags/__init__.py b/apps/archive/templatetags/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/apps/archive/templatetags/tags.py b/apps/archive/templatetags/tags.py new file mode 100755 index 0000000..68a6a9b --- /dev/null +++ b/apps/archive/templatetags/tags.py @@ -0,0 +1,19 @@ +from django import template + +register = template.Library() + +@register.simple_tag +def multiple_tags_table(tags): + return template.loader.render_to_string( + "archive/tags/multiple_tags_table.html", + {"tags": tags} + ) + + +#@register.simple_tag +#def multiple_tags_table(tags): +# return template.loader.render_to_string( +# "archive/tags/multiple_tags_table.html", +# {"tags": tags} +# ) + diff --git a/apps/archive/tests.py b/apps/archive/tests.py new file mode 100644 index 0000000..501deb7 --- /dev/null +++ b/apps/archive/tests.py @@ -0,0 +1,16 @@ +""" +This file demonstrates writing tests using the unittest module. These will pass +when you run "manage.py test". + +Replace this with more appropriate tests for your application. +""" + +from django.test import TestCase + + +class SimpleTest(TestCase): + def test_basic_addition(self): + """ + Tests that 1 + 1 always equals 2. + """ + self.assertEqual(1 + 1, 2) diff --git a/apps/archive/urls.py b/apps/archive/urls.py new file mode 100644 index 0000000..c5fe97c --- /dev/null +++ b/apps/archive/urls.py @@ -0,0 +1,19 @@ +from django.conf.urls.defaults import patterns, include, url + +urlpatterns = patterns('', + url(r'^$', 'django.views.generic.simple.redirect_to', {'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'^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'^cancel/(\d+)/$', 'archive.views.cancel_publishing', name="cancel_publishing"), + + url(r'^unmanaged/$', 'archive.views.list_unmanaged', name="list_unmanaged"), + url(r'^unmanaged/([^/]+)/$', 'archive.views.file_unmanaged', name="file_unmanaged"), + url(r'^move_to_new/([^/]+)/$', 'archive.views.move_to_new', name="move_to_new"), +) diff --git a/apps/archive/utils.py b/apps/archive/utils.py new file mode 100755 index 0000000..4710c20 --- /dev/null +++ b/apps/archive/utils.py @@ -0,0 +1,13 @@ +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 diff --git a/apps/archive/views.py b/apps/archive/views.py new file mode 100644 index 0000000..54bb28f --- /dev/null +++ b/apps/archive/views.py @@ -0,0 +1,170 @@ +# Create your views here. + +from datetime import datetime +import os +import os.path + +from archive import settings +from django.core.urlresolvers import reverse +from django.http import Http404 +from django.shortcuts import render, redirect, get_object_or_404 +from django.views.decorators.http import require_POST + +import mutagen + +from archive import models +from archive.forms import AudiobookForm + + +def list_new(request): + division = 'new' + + path = settings.NEW_PATH + objects = sorted(os.listdir(path)) + return render(request, "archive/list_new.html", locals()) + + +def file_new(request, filename): + division = 'new' + + filepath = os.path.join(settings.NEW_PATH, filename) + 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(filepath) + except IOError: + raise Http404 + d = {} + 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 + print d + + if not request.POST: + form = AudiobookForm(d) + return render(request, "archive/file_new.html", locals()) + + +@require_POST +def move_to_archive(request, filename): + """ move a new file to the unmanaged files dir """ + + old_path = os.path.join(settings.NEW_PATH, filename) + if not os.path.isdir(settings.UNMANAGED_PATH): + os.makedirs(settings.UNMANAGED_PATH) + new_path = os.path.join(settings.UNMANAGED_PATH, filename) + + 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 +def move_to_new(request, filename): + """ move a unmanaged file to new files dir """ + + old_path = os.path.join(settings.UNMANAGED_PATH, filename) + if not os.path.isdir(settings.NEW_PATH): + os.makedirs(settings.NEW_PATH) + new_path = os.path.join(settings.NEW_PATH, filename) + + 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 +def publish(request, id): + """ mark file for publishing """ + audiobook = get_object_or_404(models.Audiobook, id=id) + audiobook.publish_wait = datetime.now() + audiobook.publishing_tags = audiobook.new_publish_tags() + audiobook.save() + return redirect(file_managed, id) + +@require_POST +def cancel_publishing(request, id): + """ cancel scheduled publishing """ + audiobook = get_object_or_404(models.Audiobook, id=id) + if not audiobook.publishing: + audiobook.publish_wait = None + audiobook.publishing_tags = None + audiobook.save() + return redirect(file_managed, id) + + +def list_unpublished(request): + division = 'unpublished' + + objects = models.Audiobook.objects.filter(published=None) + return render(request, "archive/list_unpublished.html", locals()) + + + +def file_managed(request, id): + audiobook = get_object_or_404(models.Audiobook, id=id) + division = 'published' if audiobook.published else 'unpublished' + + # for tags update + tags = mutagen.File(audiobook.source_file.path) + form = AudiobookForm(instance=audiobook) + + return render(request, "archive/file_managed.html", locals()) + + + +def list_published(request): + division = 'published' + + objects = models.Audiobook.objects.exclude(published=None) + return render(request, "archive/list_published.html", locals()) + + + + +def list_unmanaged(request): + division = 'unmanaged' + + objects = sorted(os.listdir(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)) + err_exists = request.GET.get('exists') + return render(request, "archive/file_unmanaged.html", locals()) + diff --git a/audiobooks/__init__.py b/audiobooks/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/audiobooks/manage.py b/audiobooks/manage.py new file mode 100755 index 0000000..3e4eedc --- /dev/null +++ b/audiobooks/manage.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python +from django.core.management import execute_manager +import imp +try: + imp.find_module('settings') # Assumed to be in the same directory. +except ImportError: + import sys + sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n" % __file__) + sys.exit(1) + +import settings + +if __name__ == "__main__": + execute_manager(settings) diff --git a/audiobooks/settings.py b/audiobooks/settings.py new file mode 100644 index 0000000..9f0731a --- /dev/null +++ b/audiobooks/settings.py @@ -0,0 +1,163 @@ +# 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 = '' + +# 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.contrib.messages.middleware.MessageMiddleware', +) + +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', + + '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, + 'handlers': { + 'mail_admins': { + 'level': 'ERROR', + 'class': 'django.utils.log.AdminEmailHandler' + } + }, + 'loggers': { + 'django.request': { + 'handlers': ['mail_admins'], + 'level': 'ERROR', + 'propagate': True, + }, + } +} + + + +try: + from localsettings import * +except: + pass diff --git a/audiobooks/urls.py b/audiobooks/urls.py new file mode 100644 index 0000000..2cb91e0 --- /dev/null +++ b/audiobooks/urls.py @@ -0,0 +1,12 @@ +from django.conf.urls.defaults import patterns, include, url + +# Uncomment the next two lines to enable the admin: +from django.contrib import admin +admin.autodiscover() + +urlpatterns = patterns('', + url(r'^$', 'django.views.generic.simple.redirect_to', {'url': 'archive/'}), + url(r'^archive/', include('archive.urls')), + + url(r'^admin/', include(admin.site.urls)), +) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..7b29e99 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +django>=1.3 +django-jsonfield +South>=0.7 + +mutagen