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 field = "%s_file" % cls.ext
51 getattr(audiobook, field).save(
52 "%d.%s" % (audiobook.pk, cls.ext),
53 ExistingFile(file_name),
56 Audiobook.objects.filter(pk=audiobook.pk).update(
57 **{field: getattr(audiobook, field)})
60 def published(cls, audiobook):
62 "%s_published_tags" % cls.ext:
63 getattr(audiobook, "%s_tags" % cls.ext),
64 "%s_tags" % cls.ext: None,
65 "%s_published" % cls.ext: datetime.now(),
66 '%s_status' % cls.ext: None,
68 Audiobook.objects.filter(pk=audiobook.pk).update(**kwargs)
71 def put(cls, audiobook):
72 tags = getattr(audiobook, "%s_tags" % cls.ext)
73 prefix, slug = tags['url'].rstrip('/').rsplit('/', 1)
75 path = getattr(audiobook, "%s_file" % cls.ext).path
76 api.put(path, UPLOAD_PATH)
77 command = UPLOAD_CMD + (u' %s %s %s > output.txt' % (
78 pipes.quote(os.path.join(UPLOAD_PATH, os.path.basename(path))),
83 api.sudo(command, user=UPLOAD_SUDO, shell=False)
88 audiobook = Audiobook.objects.get(id=aid)
89 self.set_status(audiobook, status.ENCODING)
92 os.makedirs(BUILD_PATH)
94 if e.errno == errno.EEXIST:
99 out_file = NamedTemporaryFile(delete=False, prefix='audiobook-', suffix='.%s' % self.ext, dir=BUILD_PATH)
101 self.encode(audiobook.source_file.path, out_file.name)
102 self.set_status(audiobook, status.TAGGING)
103 self.set_tags(audiobook, out_file.name)
104 self.save(audiobook, out_file.name)
105 self.set_status(audiobook, status.SENDING)
109 self.published(audiobook)
112 class Mp3Task(AudioFormatTask):
115 # these shouldn't be staticmethods
116 def id3_text(tag, text):
117 return tag(encoding=1, text=text)
118 def id3_url(tag, text):
120 def id3_comment(tag, text, lang=u'pol'):
121 return tag(encoding=1, lang=lang, desc=u'', text=text)
122 def id3_sha1(tag, text, what=u''):
123 return tag(owner='http://wolnelektury.pl?%s' % what, data=text)
126 'album': (id3_text, id3.TALB),
127 'albumartist': (id3_text, id3.TPE2),
128 'artist': (id3_text, id3.TPE1),
129 'conductor': (id3_text, id3.TPE3),
130 'copyright': (id3_text, id3.TCOP),
131 'date': (id3_text, id3.TDRC),
132 'genre': (id3_text, id3.TCON),
133 'language': (id3_text, id3.TLAN),
134 'organization': (id3_text, id3.TPUB),
135 'title': (id3_text, id3.TIT2),
136 'comment': (id3_comment, id3.COMM, 'pol'),
137 'contact': (id3_url, id3.WOAF),
138 'license': (id3_url, id3.WCOP),
139 'flac_sha1': (id3_sha1, id3.PRIV, 'flac_sha1'),
143 def encode(in_path, out_path):
144 # 44.1kHz 64kbps mono MP3
145 subprocess.check_call(['ffmpeg',
155 def set_tags(cls, audiobook, file_name):
156 audio = id3.ID3(file_name)
157 for k, v in audiobook.mp3_tags['tags'].items():
158 factory_tuple = cls.TAG_MAP[k]
159 factory, tagtype = factory_tuple[:2]
160 audio.add(factory(tagtype, v, *factory_tuple[2:]))
163 mime = mimetypes.guess_type(COVER_IMAGE)
164 f = open(COVER_IMAGE)
165 audio.add(id3.APIC(encoding=0, mime=mime, type=3, desc=u'', data=f.read()))
171 class OggTask(AudioFormatTask):
175 def encode(in_path, out_path):
176 # 44.1kHz 64kbps mono Ogg Vorbis
177 subprocess.check_call(['oggenc',
179 '--discard-comments',
180 '--resample', '44100',