From d64f05ea901ccb7e61eacd9a5579bb81138b193d Mon Sep 17 00:00:00 2001 From: Radek Czajka Date: Fri, 5 Aug 2011 11:06:16 +0200 Subject: [PATCH 1/1] celery tasks and some other changes --- apps/archive/constants.py | 14 ++ apps/archive/cover.png | Bin 0 -> 13543 bytes apps/archive/forms.py | 11 +- ...shing_tags__del_field_audiobook_publish.py | 139 ++++++++++++++ ...ce_sha1__add_field_audiobook_translator.py | 84 +++++++++ apps/archive/models.py | 57 +++++- apps/archive/settings.py | 68 +++++++ apps/archive/static/style.css | 10 + apps/archive/tasks.py | 178 ++++++++++++++++++ apps/archive/templates/archive/base.html | 25 +-- .../templates/archive/file_managed.html | 82 ++++---- .../templates/archive/list_published.html | 2 +- .../archive/tags/multiple_tags_table.html | 8 +- .../templates/archive/tags/tags_table.html | 7 + apps/archive/templates/base.html | 24 +++ .../archive/templates/registration/login.html | 12 ++ apps/archive/templatetags/tags.py | 19 +- apps/archive/utils.py | 9 + apps/archive/views.py | 87 ++++++--- audiobooks/settings.py | 17 +- audiobooks/urls.py | 8 + requirements.txt | 5 + 22 files changed, 757 insertions(+), 109 deletions(-) create mode 100755 apps/archive/constants.py create mode 100644 apps/archive/cover.png create mode 100644 apps/archive/migrations/0002_auto__del_field_audiobook_publishing_tags__del_field_audiobook_publish.py create mode 100644 apps/archive/migrations/0003_auto__add_field_audiobook_source_sha1__add_field_audiobook_translator.py create mode 100755 apps/archive/tasks.py create mode 100755 apps/archive/templates/archive/tags/tags_table.html create mode 100755 apps/archive/templates/base.html create mode 100755 apps/archive/templates/registration/login.html diff --git a/apps/archive/constants.py b/apps/archive/constants.py new file mode 100755 index 0000000..17f4e78 --- /dev/null +++ b/apps/archive/constants.py @@ -0,0 +1,14 @@ +from django.utils.translation import ugettext_lazy as _ + +class status: + WAITING = 1 + ENCODING = 2 + TAGGING = 3 + SENDING = 4 + + choices = [ + (WAITING, _('Waiting')), + (ENCODING, _('Encoding')), + (TAGGING, _('Tagging')), + (SENDING, _('Sending')), + ] diff --git a/apps/archive/cover.png b/apps/archive/cover.png new file mode 100644 index 0000000000000000000000000000000000000000..1cdf8e1d8bca9441db681193c187edef7822c5ae GIT binary patch literal 13543 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 NP*Kp3ua-3r{U7TeU output.txt' % ( + pipes.quote(os.path.join(UPLOAD_PATH, os.path.basename(path))), + pipes.quote(slug), + pipes.quote(name) + )).encode('utf-8') + print command + if UPLOAD_SUDO: + api.sudo(command, user=UPLOAD_SUDO, shell=False) + else: + api.run(command) + + def run(self, aid): + audiobook = Audiobook.objects.get(id=aid) + self.set_status(audiobook, 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='audiobook-', suffix='.%s' % self.ext, dir=BUILD_PATH) + out_file.close() + self.encode(audiobook.source_file.path, out_file.name) + self.set_status(audiobook, status.TAGGING) + self.set_tags(audiobook, out_file.name) + self.save(audiobook, out_file.name) + self.set_status(audiobook, status.SENDING) + + #self.put(audiobook) + + self.published(audiobook) + audiobook.save() + + +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_sha1(tag, text, what=u''): + return tag(owner='http://wolnelektury.pl?%s' % what, data=text) + + 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_sha1, id3.PRIV, 'flac_sha1'), + } + + @staticmethod + def encode(in_path, out_path): + # 44.1kHz 64kbps mono MP3 + subprocess.check_call(['ffmpeg', + '-i', in_path, + '-ar', '44100', + '-ab', '64k', + '-ac', '1', + '-y', + out_path + ]) + + @classmethod + def set_tags(cls, audiobook, file_name): + audio = id3.ID3(file_name) + for k, v in audiobook.mp3_tags['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(['oggenc', + in_path, + '--discard-comments', + '--resample', '44100', + '--downmix', + '-b', '64', + '-o', out_path + ]) diff --git a/apps/archive/templates/archive/base.html b/apps/archive/templates/archive/base.html index 807227b..fbc5a18 100644 --- a/apps/archive/templates/archive/base.html +++ b/apps/archive/templates/archive/base.html @@ -1,28 +1,11 @@ +{% extends "base.html" %} {% load i18n %} - - - - - - {% trans "Audiobook repository" %} - - - - -
-{% block messages %}{% endblock %} -
- -
-{% block content %}{% endblock %} -
- - - +{% endblock %} diff --git a/apps/archive/templates/archive/file_managed.html b/apps/archive/templates/archive/file_managed.html index 897aae2..c67edea 100755 --- a/apps/archive/templates/archive/file_managed.html +++ b/apps/archive/templates/archive/file_managed.html @@ -1,37 +1,46 @@ {% extends "archive/base.html" %} {% load i18n %} +{% load tags %} {% block content %}

{% trans "Publishing" %}

-{% if audiobook.publish_wait %} -

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

+{% if audiobook.mp3_status or audiobook.ogg_status %} - - {% for t, v in audiobook.publishing_tags.items %} - - {% endfor %} -
{{ t }}{{ v }}
+

{% trans "Publishing pending" %}

- {% if audiobook.publishing %} -

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

- {% else %} -
- {% csrf_token %} - -
- {% endif %} -{% else %} - {% if audiobook.published %} - Here be currently published version, for comparison. - {% endif %} +
+ {% csrf_token %} + +
+ + +{% if audiobook.mp3_status %} +
+

MP3

+ + {% tags_table audiobook.mp3_tags.tags %} + +

Status: {{ audiobook.get_mp3_status_display }}

+{% endif %} + +{% if audiobook.ogg_status %} +
+

Ogg Vorbis

+ + {% tags_table audiobook.ogg_tags.tags %} +

Status: {{ audiobook.get_ogg_status_display }}

+{% endif %} + + + + +{% else %}
- - {% for k, v in audiobook.new_publish_tags.items %} - - {% endfor %} +
{{ k }}{{ v }}
+ {% tags_table audiobook.new_publish_tags 0 %} {% csrf_token %} @@ -39,6 +48,22 @@ {% endif %} +
+{% if audiobook.mp3_published %} +

Published MP3

+ {{ audiobook.mp3_published }} + {% tags_table audiobook.mp3_published_tags.tags %} +{% else %}

MP3 file hasn't been published yet.

+{% endif %} + +
+{% if audiobook.ogg_published %} +

Published Ogg Vorbis

+ {{ audiobook.ogg_published }} + {% tags_table audiobook.ogg_published_tags.tags %} +{% else %}Ogg Vorbis file hasn't been published yet. +{% endif %} + @@ -49,16 +74,9 @@

{% trans "Update tags" %}

+Last modified: {{ audiobook.modified }} -
- {% for t, v in tags.items %} - - {% endfor %} -
{{t}} - {% for x in v %} - {{x}}
- {% endfor %} -
+{% multiple_tags_table tags %} diff --git a/apps/archive/templates/archive/list_published.html b/apps/archive/templates/archive/list_published.html index c51cace..49544b4 100755 --- a/apps/archive/templates/archive/list_published.html +++ b/apps/archive/templates/archive/list_published.html @@ -14,7 +14,7 @@ {% block file-list %} {% for file in objects %}
  • - {{ file }} + {{ 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 index 4a39808..4609278 100755 --- a/apps/archive/templates/archive/tags/multiple_tags_table.html +++ b/apps/archive/templates/archive/tags/multiple_tags_table.html @@ -1,9 +1,9 @@ - +{% if table %}
    {% endif %} {% for t, v in tags.items %} - {% endfor %} -
    {{t}} +
    {{ t }} {% for x in v %} -
    {{x}}
    +
    {{ x|linebreaksbr }}
    {% endfor %}
    +{% if table %}{% endif %} diff --git a/apps/archive/templates/archive/tags/tags_table.html b/apps/archive/templates/archive/tags/tags_table.html new file mode 100755 index 0000000..70b9d48 --- /dev/null +++ b/apps/archive/templates/archive/tags/tags_table.html @@ -0,0 +1,7 @@ +{% if table %}{% endif %} + {% for t, v in tags.items %} + + {% endfor %} +{% if table %}
    {{ t }} + {{ v|linebreaksbr }} +
    {% endif %} diff --git a/apps/archive/templates/base.html b/apps/archive/templates/base.html new file mode 100755 index 0000000..c6b7ee6 --- /dev/null +++ b/apps/archive/templates/base.html @@ -0,0 +1,24 @@ +{% load i18n %} + + + + + + {% trans "Audiobook repository" %} + + + +
    + {% block repo-zones-nav %} {% endblock %} +
    + +
    +{% block messages %}{% endblock %} +
    + +
    +{% block content %}{% endblock %} +
    + + + diff --git a/apps/archive/templates/registration/login.html b/apps/archive/templates/registration/login.html new file mode 100755 index 0000000..115cdf1 --- /dev/null +++ b/apps/archive/templates/registration/login.html @@ -0,0 +1,12 @@ +{% extends "base.html" %} +{% load i18n %} + +{% block content %} + +
    +{% csrf_token %} +{{ form.as_p }} +

    +
    + +{% endblock %} diff --git a/apps/archive/templatetags/tags.py b/apps/archive/templatetags/tags.py index 68a6a9b..25389ef 100755 --- a/apps/archive/templatetags/tags.py +++ b/apps/archive/templatetags/tags.py @@ -2,18 +2,11 @@ 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.inclusion_tag('archive/tags/multiple_tags_table.html') +def multiple_tags_table(tags, table=True): + return locals() -#@register.simple_tag -#def multiple_tags_table(tags): -# return template.loader.render_to_string( -# "archive/tags/multiple_tags_table.html", -# {"tags": tags} -# ) - +@register.inclusion_tag('archive/tags/tags_table.html') +def tags_table(tags, table=True): + return locals() diff --git a/apps/archive/utils.py b/apps/archive/utils.py index 4710c20..6e0a8d1 100755 --- a/apps/archive/utils.py +++ b/apps/archive/utils.py @@ -1,5 +1,7 @@ +from hashlib import sha1 from django.core.files.uploadedfile import UploadedFile + class ExistingFile(UploadedFile): def __init__(self, path, *args, **kwargs): @@ -11,3 +13,10 @@ class ExistingFile(UploadedFile): def close(self): pass + + +def sha1_file(f): + sha = sha1() + for piece in iter(lambda: f.read(1024*1024), ''): + sha.update(piece) + return sha.hexdigest() diff --git a/apps/archive/views.py b/apps/archive/views.py index 3ee87cb..575f5d9 100644 --- a/apps/archive/views.py +++ b/apps/archive/views.py @@ -5,17 +5,23 @@ import os import os.path from archive import settings +from django.contrib.auth import logout +from django.contrib.auth.decorators import login_required from django.core.urlresolvers import reverse +from django.db.models import Q 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.constants import status from archive import models from archive.forms import AudiobookForm +from archive import tasks +@login_required def list_new(request): division = 'new' @@ -24,6 +30,7 @@ def list_new(request): return render(request, "archive/list_new.html", locals()) +@login_required def file_new(request, filename): division = 'new' @@ -60,6 +67,7 @@ def file_new(request, filename): @require_POST +@login_required def move_to_archive(request, filename): """ move a new file to the unmanaged files dir """ @@ -84,6 +92,7 @@ def move_to_archive(request, filename): @require_POST +@login_required def move_to_new(request, filename): """ move a unmanaged file to new files dir """ @@ -106,37 +115,70 @@ def move_to_new(request, filename): return redirect(list_unmanaged) + @require_POST -def publish(request, id): +@login_required +def publish(request, aid): """ 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 = 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() - return redirect(file_managed, id) + # isn't there a race here? + audiobook.mp3_task = tasks.Mp3Task.delay(aid).task_id + audiobook.ogg_task = tasks.OggTask.delay(aid).task_id + audiobook.save() + + return redirect(file_managed, aid) + @require_POST -def cancel_publishing(request, id): +@login_required +def cancel_publishing(request, aid): """ 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) + 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) +@login_required def list_unpublished(request): division = 'unpublished' - objects = models.Audiobook.objects.filter(published=None) + objects = models.Audiobook.objects.filter(Q(mp3_published=None) | Q(ogg_published=None)) return render(request, "archive/list_unpublished.html", locals()) +@login_required +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()) + +@login_required def file_managed(request, id): audiobook = get_object_or_404(models.Audiobook, id=id) - division = 'published' if audiobook.published else 'unpublished' + + 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' # for tags update tags = mutagen.File(audiobook.source_file.path) @@ -145,16 +187,7 @@ def file_managed(request, id): 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()) - - - - +@login_required def list_unmanaged(request): division = 'unmanaged' @@ -162,6 +195,7 @@ def list_unmanaged(request): return render(request, "archive/list_unmanaged.html", locals()) +@login_required def file_unmanaged(request, filename): division = 'unmanaged' @@ -169,3 +203,8 @@ def file_unmanaged(request, filename): err_exists = request.GET.get('exists') return render(request, "archive/file_unmanaged.html", locals()) + +@login_required +def logout_view(request): + logout(request) + return redirect(list_new) diff --git a/audiobooks/settings.py b/audiobooks/settings.py index 9f0731a..dc55333 100644 --- a/audiobooks/settings.py +++ b/audiobooks/settings.py @@ -58,7 +58,7 @@ 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_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 @@ -128,6 +128,9 @@ INSTALLED_APPS = ( # Uncomment the next line to enable admin documentation: # 'django.contrib.admindocs', + 'djcelery', + 'djkombu', + 'south', 'archive', ) @@ -157,6 +160,18 @@ LOGGING = { + +import djcelery +djcelery.setup_loader() + +BROKER_BACKEND = "djkombu.transport.DatabaseTransport" +BROKER_HOST = "localhost" +BROKER_PORT = 5672 +BROKER_USER = "guest" +BROKER_PASSWORD = "guest" +BROKER_VHOST = "/" + + try: from localsettings import * except: diff --git a/audiobooks/urls.py b/audiobooks/urls.py index 2cb91e0..76efbb3 100644 --- a/audiobooks/urls.py +++ b/audiobooks/urls.py @@ -1,3 +1,4 @@ +from django.conf import settings from django.conf.urls.defaults import patterns, include, url # Uncomment the next two lines to enable the admin: @@ -9,4 +10,11 @@ urlpatterns = patterns('', url(r'^archive/', include('archive.urls')), url(r'^admin/', include(admin.site.urls)), + url(r'^accounts/login/', 'django.contrib.auth.views.login'), + url(r'^accounts/logout/', 'archive.views.logout_view', name='logout'), +) + +if settings.DEBUG: + urlpatterns += patterns('', + (r'^media/(?P.*)$', 'django.views.static.serve', {'document_root': settings.MEDIA_ROOT, 'show_indexes':True}), ) diff --git a/requirements.txt b/requirements.txt index 7b29e99..899e65b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,9 @@ django>=1.3 django-jsonfield South>=0.7 +django-celery +django-kombu + +paramiko>=1.7.7.1 # fabric dependency with http://code.fabfile.org/issues/show/214 fixed +fabric mutagen -- 2.20.1