1 from datetime import datetime
8 from tempfile import NamedTemporaryFile
11 #from celery.decorators import task
12 from celery.task import Task
13 from django.db.models import F
14 from fabric import api
15 from fabric.network import disconnect_all
16 from mutagen import File
17 from mutagen import id3
21 from archive.constants import status
22 from archive.models import Audiobook
23 from archive.settings import (BUILD_PATH, COVER_IMAGE,
24 UPLOAD_HOST, UPLOAD_USER, UPLOAD_PASSWORD, UPLOAD_PATH, UPLOAD_CMD, UPLOAD_SUDO)
25 from archive.utils import ExistingFile
27 api.env.host_string = UPLOAD_HOST
28 api.env.user = UPLOAD_USER
29 api.env.password = UPLOAD_PASSWORD
31 class AudioFormatTask(Task):
34 class RemoteOperationError(BaseException):
38 def set_status(cls, aid, status):
39 Audiobook.objects.filter(pk=aid).update(
40 **{'%s_status' % cls.ext: status})
43 def encode(in_path, out_path):
47 def set_tags(cls, audiobook, file_name):
48 audio = File(file_name)
49 for k, v in getattr(audiobook, "%s_tags" % cls.ext)['tags'].items():
54 def save(cls, audiobook, file_name):
55 field = "%s_file" % cls.ext
56 getattr(audiobook, field).save(
57 "%d.%s" % (audiobook.pk, cls.ext),
58 ExistingFile(file_name),
61 Audiobook.objects.filter(pk=audiobook.pk).update(
62 **{field: getattr(audiobook, field)})
65 def published(cls, aid):
67 "%s_published_tags" % cls.ext: F("%s_tags" % cls.ext),
68 "%s_tags" % cls.ext: None,
69 "%s_published" % cls.ext: datetime.now(),
70 '%s_status' % cls.ext: None,
72 Audiobook.objects.filter(pk=aid).update(**kwargs)
75 def put(cls, audiobook, path):
76 tags = getattr(audiobook, "%s_tags" % cls.ext)
77 prefix, slug = tags['url'].rstrip('/').rsplit('/', 1)
79 command = UPLOAD_CMD + (u' %s %s %s > output.txt' % (
80 pipes.quote(os.path.join(UPLOAD_PATH, os.path.basename(path))),
85 api.put(path, UPLOAD_PATH)
87 api.sudo(command, user=UPLOAD_SUDO, shell=False)
92 raise cls.RemoteOperationError
96 audiobook = Audiobook.objects.get(id=aid)
97 self.set_status(aid, status.ENCODING)
100 os.makedirs(BUILD_PATH)
102 if e.errno == errno.EEXIST:
107 out_file = NamedTemporaryFile(delete=False, prefix='%d-' % aid, suffix='.%s' % self.ext, dir=BUILD_PATH)
109 self.encode(audiobook.source_file.path, out_file.name)
110 self.set_status(aid, status.TAGGING)
111 self.set_tags(audiobook, out_file.name)
112 self.set_status(aid, status.SENDING)
114 self.put(audiobook, out_file.name)
116 self.save(audiobook, out_file.name)
119 def on_failure(self, exc, task_id, args, kwargs, einfo):
120 aid = (args[0], kwargs.get('aid'))[0]
121 self.set_status(aid, None)
124 class Mp3Task(AudioFormatTask):
127 # these shouldn't be staticmethods
128 def id3_text(tag, text):
129 return tag(encoding=1, text=text)
130 def id3_url(tag, text):
132 def id3_comment(tag, text, lang=u'pol'):
133 return tag(encoding=1, lang=lang, desc=u'', text=text)
134 def id3_priv(tag, text, what=u''):
135 return tag(owner='wolnelektury.pl?%s' % what, data=text.encode('utf-8'))
138 'album': (id3_text, id3.TALB),
139 'albumartist': (id3_text, id3.TPE2),
140 'artist': (id3_text, id3.TPE1),
141 'conductor': (id3_text, id3.TPE3),
142 'copyright': (id3_text, id3.TCOP),
143 'date': (id3_text, id3.TDRC),
144 'genre': (id3_text, id3.TCON),
145 'language': (id3_text, id3.TLAN),
146 'organization': (id3_text, id3.TPUB),
147 'title': (id3_text, id3.TIT2),
148 'comment': (id3_comment, id3.COMM, 'pol'),
149 'contact': (id3_url, id3.WOAF),
150 'license': (id3_url, id3.WCOP),
151 'flac_sha1': (id3_priv, id3.PRIV, 'flac_sha1'),
152 'project': (id3_priv, id3.PRIV, 'project'),
153 'funded_by': (id3_priv, id3.PRIV, 'funded_by'),
157 def encode(in_path, out_path):
158 # 44.1kHz 64kbps mono MP3
159 subprocess.check_call(['ffmpeg',
160 '-i', in_path.encode('utf-8'),
165 '-acodec', 'libmp3lame',
166 out_path.encode('utf-8')
170 def set_tags(cls, audiobook, file_name):
171 audio = id3.ID3(file_name)
172 for k, v in audiobook.mp3_tags['tags'].items():
173 factory_tuple = cls.TAG_MAP[k]
174 factory, tagtype = factory_tuple[:2]
175 audio.add(factory(tagtype, v, *factory_tuple[2:]))
178 mime = mimetypes.guess_type(COVER_IMAGE)
179 f = open(COVER_IMAGE)
180 audio.add(id3.APIC(encoding=0, mime=mime, type=3, desc=u'', data=f.read()))
186 class OggTask(AudioFormatTask):
190 def encode(in_path, out_path):
191 # 44.1kHz 64kbps mono Ogg Vorbis
192 subprocess.check_call(['ffmpeg',
193 '-i', in_path.encode('utf-8'),
198 '-acodec', 'libvorbis',
199 out_path.encode('utf-8')