1 from datetime import datetime
8 from tempfile import NamedTemporaryFile
11 #from celery.decorators import task
12 from celery.task import Task
13 from fabric import api
14 from mutagen import File
15 from mutagen import id3
19 from archive.constants import status
20 from archive.models import Audiobook
21 from archive.settings import (BUILD_PATH, COVER_IMAGE,
22 UPLOAD_HOST, UPLOAD_USER, UPLOAD_PASSWORD, UPLOAD_PATH, UPLOAD_CMD, UPLOAD_SUDO)
23 from archive.utils import ExistingFile
25 api.env.host_string = UPLOAD_HOST
26 api.env.user = UPLOAD_USER
27 api.env.password = UPLOAD_PASSWORD
29 class AudioFormatTask(Task):
33 def set_status(cls, audiobook, status):
34 Audiobook.objects.filter(pk=audiobook.pk).update(
35 **{'%s_status' % cls.ext: status})
38 def encode(in_path, out_path):
42 def set_tags(cls, audiobook, file_name):
43 audio = File(file_name)
44 for k, v in getattr(audiobook, "%s_tags" % cls.ext)['tags'].items():
49 def save(cls, audiobook, file_name):
50 getattr(audiobook, "%s_file" % cls.ext).save(
51 "%d.%s" % (audiobook.pk, cls.ext),
52 ExistingFile(file_name)
56 def published(cls, audiobook):
58 "%s_published_tags" % cls.ext:
59 getattr(audiobook, "%s_tags" % cls.ext),
60 "%s_tags" % cls.ext: None,
61 "%s_published" % cls.ext: datetime.now(),
62 '%s_status' % cls.ext: None,
64 Audiobook.objects.filter(pk=audiobook.pk).update(**kwargs)
67 def put(cls, audiobook):
68 tags = getattr(audiobook, "%s_tags" % cls.ext)
69 prefix, slug = tags['url'].rstrip('/').rsplit('/', 1)
71 path = getattr(audiobook, "%s_file" % cls.ext).path
72 api.put(path, UPLOAD_PATH)
73 command = UPLOAD_CMD + (u' %s %s %s > output.txt' % (
74 pipes.quote(os.path.join(UPLOAD_PATH, os.path.basename(path))),
79 api.sudo(command, user=UPLOAD_SUDO, shell=False)
84 audiobook = Audiobook.objects.get(id=aid)
85 self.set_status(audiobook, status.ENCODING)
88 os.makedirs(BUILD_PATH)
90 if e.errno == errno.EEXIST:
95 out_file = NamedTemporaryFile(delete=False, prefix='audiobook-', suffix='.%s' % self.ext, dir=BUILD_PATH)
97 self.encode(audiobook.source_file.path, out_file.name)
98 self.set_status(audiobook, status.TAGGING)
99 self.set_tags(audiobook, out_file.name)
100 self.save(audiobook, out_file.name)
101 self.set_status(audiobook, status.SENDING)
105 self.published(audiobook)
108 class Mp3Task(AudioFormatTask):
111 # these shouldn't be staticmethods
112 def id3_text(tag, text):
113 return tag(encoding=1, text=text)
114 def id3_url(tag, text):
116 def id3_comment(tag, text, lang=u'pol'):
117 return tag(encoding=1, lang=lang, desc=u'', text=text)
118 def id3_sha1(tag, text, what=u''):
119 return tag(owner='http://wolnelektury.pl?%s' % what, data=text)
122 'album': (id3_text, id3.TALB),
123 'albumartist': (id3_text, id3.TPE2),
124 'artist': (id3_text, id3.TPE1),
125 'conductor': (id3_text, id3.TPE3),
126 'copyright': (id3_text, id3.TCOP),
127 'date': (id3_text, id3.TDRC),
128 'genre': (id3_text, id3.TCON),
129 'language': (id3_text, id3.TLAN),
130 'organization': (id3_text, id3.TPUB),
131 'title': (id3_text, id3.TIT2),
132 'comment': (id3_comment, id3.COMM, 'pol'),
133 'contact': (id3_url, id3.WOAF),
134 'license': (id3_url, id3.WCOP),
135 'flac_sha1': (id3_sha1, id3.PRIV, 'flac_sha1'),
139 def encode(in_path, out_path):
140 # 44.1kHz 64kbps mono MP3
141 subprocess.check_call(['ffmpeg',
151 def set_tags(cls, audiobook, file_name):
152 audio = id3.ID3(file_name)
153 for k, v in audiobook.mp3_tags['tags'].items():
154 factory_tuple = cls.TAG_MAP[k]
155 factory, tagtype = factory_tuple[:2]
156 audio.add(factory(tagtype, v, *factory_tuple[2:]))
159 mime = mimetypes.guess_type(COVER_IMAGE)
160 f = open(COVER_IMAGE)
161 audio.add(id3.APIC(encoding=0, mime=mime, type=3, desc=u'', data=f.read()))
167 class OggTask(AudioFormatTask):
171 def encode(in_path, out_path):
172 # 44.1kHz 64kbps mono Ogg Vorbis
173 subprocess.check_call(['oggenc',
175 '--discard-comments',
176 '--resample', '44100',