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 fabric.network import disconnect_all
15 from mutagen import File
16 from mutagen import id3
20 from archive.constants import status
21 from archive.models import Audiobook
22 from archive.settings import (BUILD_PATH, COVER_IMAGE,
23 UPLOAD_HOST, UPLOAD_USER, UPLOAD_PASSWORD, UPLOAD_PATH, UPLOAD_CMD, UPLOAD_SUDO)
24 from archive.utils import ExistingFile
26 api.env.host_string = UPLOAD_HOST
27 api.env.user = UPLOAD_USER
28 api.env.password = UPLOAD_PASSWORD
30 class AudioFormatTask(Task):
34 def set_status(cls, audiobook, status):
35 Audiobook.objects.filter(pk=audiobook.pk).update(
36 **{'%s_status' % cls.ext: status})
39 def encode(in_path, out_path):
43 def set_tags(cls, audiobook, file_name):
44 audio = File(file_name)
45 for k, v in getattr(audiobook, "%s_tags" % cls.ext)['tags'].items():
50 def save(cls, audiobook, file_name):
51 field = "%s_file" % cls.ext
52 getattr(audiobook, field).save(
53 "%d.%s" % (audiobook.pk, cls.ext),
54 ExistingFile(file_name),
57 Audiobook.objects.filter(pk=audiobook.pk).update(
58 **{field: getattr(audiobook, field)})
61 def published(cls, audiobook):
63 "%s_published_tags" % cls.ext:
64 getattr(audiobook, "%s_tags" % cls.ext),
65 "%s_tags" % cls.ext: None,
66 "%s_published" % cls.ext: datetime.now(),
67 '%s_status' % cls.ext: None,
69 Audiobook.objects.filter(pk=audiobook.pk).update(**kwargs)
72 def put(cls, audiobook):
73 tags = getattr(audiobook, "%s_tags" % cls.ext)
74 prefix, slug = tags['url'].rstrip('/').rsplit('/', 1)
76 path = getattr(audiobook, "%s_file" % cls.ext).path
77 api.put(path, UPLOAD_PATH)
78 command = UPLOAD_CMD + (u' %s %s %s > output.txt' % (
79 pipes.quote(os.path.join(UPLOAD_PATH, os.path.basename(path))),
84 api.sudo(command, user=UPLOAD_SUDO, shell=False)
90 audiobook = Audiobook.objects.get(id=aid)
91 self.set_status(audiobook, status.ENCODING)
94 os.makedirs(BUILD_PATH)
96 if e.errno == errno.EEXIST:
101 out_file = NamedTemporaryFile(delete=False, prefix='audiobook-', suffix='.%s' % self.ext, dir=BUILD_PATH)
103 self.encode(audiobook.source_file.path, out_file.name)
104 self.set_status(audiobook, status.TAGGING)
105 self.set_tags(audiobook, out_file.name)
106 self.save(audiobook, out_file.name)
107 self.set_status(audiobook, status.SENDING)
111 self.published(audiobook)
114 class Mp3Task(AudioFormatTask):
117 # these shouldn't be staticmethods
118 def id3_text(tag, text):
119 return tag(encoding=1, text=text)
120 def id3_url(tag, text):
122 def id3_comment(tag, text, lang=u'pol'):
123 return tag(encoding=1, lang=lang, desc=u'', text=text)
124 def id3_sha1(tag, text, what=u''):
125 return tag(owner='http://wolnelektury.pl?%s' % what, data=text)
128 'album': (id3_text, id3.TALB),
129 'albumartist': (id3_text, id3.TPE2),
130 'artist': (id3_text, id3.TPE1),
131 'conductor': (id3_text, id3.TPE3),
132 'copyright': (id3_text, id3.TCOP),
133 'date': (id3_text, id3.TDRC),
134 'genre': (id3_text, id3.TCON),
135 'language': (id3_text, id3.TLAN),
136 'organization': (id3_text, id3.TPUB),
137 'title': (id3_text, id3.TIT2),
138 'comment': (id3_comment, id3.COMM, 'pol'),
139 'contact': (id3_url, id3.WOAF),
140 'license': (id3_url, id3.WCOP),
141 'flac_sha1': (id3_sha1, id3.PRIV, 'flac_sha1'),
145 def encode(in_path, out_path):
146 # 44.1kHz 64kbps mono MP3
147 subprocess.check_call(['ffmpeg',
157 def set_tags(cls, audiobook, file_name):
158 audio = id3.ID3(file_name)
159 for k, v in audiobook.mp3_tags['tags'].items():
160 factory_tuple = cls.TAG_MAP[k]
161 factory, tagtype = factory_tuple[:2]
162 audio.add(factory(tagtype, v, *factory_tuple[2:]))
165 mime = mimetypes.guess_type(COVER_IMAGE)
166 f = open(COVER_IMAGE)
167 audio.add(id3.APIC(encoding=0, mime=mime, type=3, desc=u'', data=f.read()))
173 class OggTask(AudioFormatTask):
177 def encode(in_path, out_path):
178 # 44.1kHz 64kbps mono Ogg Vorbis
179 subprocess.check_call(['oggenc',
181 '--discard-comments',
182 '--resample', '44100',