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 setattr(audiobook, '%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):
57 setattr(audiobook, "%s_published_tags" % cls.ext,
58 getattr(audiobook, "%s_tags" % cls.ext))
59 setattr(audiobook, "%s_tags" % cls.ext, None)
60 setattr(audiobook, "%s_published" % cls.ext, datetime.now())
61 cls.set_status(audiobook, None)
64 def put(cls, audiobook):
65 tags = getattr(audiobook, "%s_tags" % cls.ext)
66 prefix, slug = tags['url'].rstrip('/').rsplit('/', 1)
68 path = getattr(audiobook, "%s_file" % cls.ext).path
69 api.put(path, UPLOAD_PATH)
70 command = UPLOAD_CMD + (u' %s %s %s > output.txt' % (
71 pipes.quote(os.path.join(UPLOAD_PATH, os.path.basename(path))),
77 api.sudo(command, user=UPLOAD_SUDO, shell=False)
82 audiobook = Audiobook.objects.get(id=aid)
83 self.set_status(audiobook, status.ENCODING)
86 os.makedirs(BUILD_PATH)
88 if e.errno == errno.EEXIST:
93 out_file = NamedTemporaryFile(delete=False, prefix='audiobook-', suffix='.%s' % self.ext, dir=BUILD_PATH)
95 self.encode(audiobook.source_file.path, out_file.name)
96 self.set_status(audiobook, status.TAGGING)
97 self.set_tags(audiobook, out_file.name)
98 self.save(audiobook, out_file.name)
99 self.set_status(audiobook, status.SENDING)
103 self.published(audiobook)
107 class Mp3Task(AudioFormatTask):
110 # these shouldn't be staticmethods
111 def id3_text(tag, text):
112 return tag(encoding=1, text=text)
113 def id3_url(tag, text):
115 def id3_comment(tag, text, lang=u'pol'):
116 return tag(encoding=1, lang=lang, desc=u'', text=text)
117 def id3_sha1(tag, text, what=u''):
118 return tag(owner='http://wolnelektury.pl?%s' % what, data=text)
121 'album': (id3_text, id3.TALB),
122 'albumartist': (id3_text, id3.TPE2),
123 'artist': (id3_text, id3.TPE1),
124 'conductor': (id3_text, id3.TPE3),
125 'copyright': (id3_text, id3.TCOP),
126 'date': (id3_text, id3.TDRC),
127 'genre': (id3_text, id3.TCON),
128 'language': (id3_text, id3.TLAN),
129 'organization': (id3_text, id3.TPUB),
130 'title': (id3_text, id3.TIT2),
131 'comment': (id3_comment, id3.COMM, 'pol'),
132 'contact': (id3_url, id3.WOAF),
133 'license': (id3_url, id3.WCOP),
134 'flac_sha1': (id3_sha1, id3.PRIV, 'flac_sha1'),
138 def encode(in_path, out_path):
139 # 44.1kHz 64kbps mono MP3
140 subprocess.check_call(['ffmpeg',
150 def set_tags(cls, audiobook, file_name):
151 audio = id3.ID3(file_name)
152 for k, v in audiobook.mp3_tags['tags'].items():
153 factory_tuple = cls.TAG_MAP[k]
154 factory, tagtype = factory_tuple[:2]
155 audio.add(factory(tagtype, v, *factory_tuple[2:]))
158 mime = mimetypes.guess_type(COVER_IMAGE)
159 f = open(COVER_IMAGE)
160 audio.add(id3.APIC(encoding=0, mime=mime, type=3, desc=u'', data=f.read()))
166 class OggTask(AudioFormatTask):
170 def encode(in_path, out_path):
171 # 44.1kHz 64kbps mono Ogg Vorbis
172 subprocess.check_call(['oggenc',
174 '--discard-comments',
175 '--resample', '44100',