2 # This file is part of mp3togo
4 # Convert audio files to play on a mp3 player
5 # Manage the transform of a single file
7 # (c) Simeon Veldstra 2006 <reallifesim@gmail.com>
9 # This software is free.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You may redistribute this program under the terms of the
17 # GNU General Public Licence version 2
18 # Available in this package or at http://www.fsf.org
25 import mp3togo.conf as conf
26 import mp3togo.tags as tags
27 import mp3togo.task as task
28 import mp3togo.cache as cache
29 from mp3togo.helpers import helpers
31 #helpers = mp3togo.helpers.helpers
45 """Encapsulate the transformation of a file."""
47 def __init__(self, filename, opts, cooked=None, intags=None):
50 self._filename = filename
57 if os.path.exists(self._wavname):
58 os.unlink(self._wavname)
62 if os.path.exists(self._outname):
63 os.unlink(self._outname)
68 if os.path.exists(self._outname):
69 self.bytes = os.stat(self._outname).st_size
79 job = task.SimpleTask(self, start, None, abort, name="Start")
84 # Do this early so that 'treestructure' can access them
88 self.tags = tags.Tags(filename, opts)
92 filetype = opts.getfiletype(filename)
93 dir, base = os.path.split(filename)
94 base = os.path.splitext(base)[0]
95 base = base + opts['brwarning']
96 base = opts.cleanfilename(base)
97 if opts['treestructure'] != '':
98 # Standard format string
99 # See tags.Tags.format.__doc__
100 fmt = opts['treestructure']
101 dest = self.tags.format(fmt)
102 dest = opts.cleanfilename(dest)
104 self._outdir = os.path.dirname(dest)
105 self._outdir = os.path.join(opts['playerdir'], self._outdir)
106 self._outname = os.path.join(opts['playerdir'], dest)
108 if opts['treedepth']:
109 head, dir = os.path.split(dir)
110 for i in range(opts['treedepth'] -1):
111 head, tail = os.path.split(head)
112 dir = os.path.join(tail, dir)
113 dir = opts.cleanfilename(dir)
116 self._outdir = os.path.join(opts['playerdir'], dir)
117 self._outname = os.path.join(self._outdir, base)
118 self._outname += '.' + helpers[opts['encoder']]['type']
119 self._wavname = os.path.join(opts['tempdir'], base) + '.wav'
122 if not os.path.isdir(self._outdir):
123 os.makedirs(self._outdir)
127 if opts['treedepth']:
129 for i in range(opts['treedepth']):
134 tail, head = os.path.split(tail)
137 job = task.SimpleTask(self, make_dirs, None,
138 rm_dirs, name="Creating dirs")
142 # Check the cache - if there is one
144 self._hash = self._cache.search(self._filename)
147 return self._cache.recover(self._hash, self._outname)
148 def undo_recover_cache():
149 if os.path.exists(self._outname):
150 os.unlink(self._outname)
152 outreq = os.stat(self._cache.file(self._hash)).st_size
153 job = task.SimpleTask(self, recover_cache, None,
155 outsize=outreq, name="Hitting Cache")
161 prog = conf.find_helper(filetype, 'decode')
162 tmpreq = opts.est_decoded_size(self._filename)
163 jobname = "Decoding %s" % filetype
164 if callable(helpers[prog]['cmd']):
166 func = helpers[prog]['cmd'](self._filename, self._wavname)
167 job = task.SimpleTask(self, func, None, undo_decode,
168 tmpsize=tmpreq, name=jobname)
171 args = conf.make_args(prog, self._filename,
173 job = task.Task(self, args, helpers[prog]['parser'],
174 undo_decode, tmpsize=tmpreq, name=jobname)
180 if opts.bin['normalize-audio']:
181 ncmd = [opts.bin['normalize-audio'], self._wavname]
182 job = task.Task(self, ncmd, lambda: '',
183 lambda: True, name="Normalizing")
187 if not opts['nonormal']:
188 opts.log(2, "'normalize-audio' binary not found, skipping.")
192 encoder = opts['encoder']
193 prog = helpers[encoder]
194 outreq = tmpreq / opts['compfactor']
195 jobname = "Encoding %s" % prog['type']
196 filter = prog['parser']
197 if callable(prog['cmd']):
198 func = prog['cmd'](self._wavname, self._outname)
199 job = task.SimpleTask(self, func, None, undo_encode,
200 outsize=outreq, name=jobname)
203 args = conf.make_args(encoder, self._wavname,
204 self._outname, opts['encopts'])
205 job = task.Task(self, args, filter,
206 undo_encode, outsize=outreq, name=jobname)
212 job = task.SimpleTask(self, undo_decode, None, undo_decode,
220 indexname = os.path.join(self._outdir, '2go.index')
222 tags.remove_from_index(self._outname, indexname)
225 self.tags.writeindex(self._outname, indexname)
227 job = task.SimpleTask(self, write_index, None,
228 undo_index, name='Writing index')
233 # tag files from the cache as well, brwarning may have changed
234 if not opts['notags']:
236 self.tags.write(self._outname)
238 job = task.SimpleTask(self, tag_output, None,
239 lambda: True, name="Tagging")
243 # Cache the output if we had to go to the trouble of producing it
244 if not self._hash and self._cache:
245 self._hash = cache.checksum(self._filename)
247 self._cache.stash(self._outname, self._hash)
249 job = task.SimpleTask(self, cache_final, name="Caching result")
253 # Completion sentinel
254 job = task.SimpleTask(self, finish, None, start, name="Done")
258 # Consider the track done if the output file exists:
259 if os.path.exists(self._outname) and not opts['force']:
260 opts.log(1, "Skipping existing file: %s\n" % self._outname)
261 self._queue = tuple(tasks)
262 if self._hash and self._cache:
263 self._cache.release(self._hash)
264 for tsk in self._queue:
265 tsk._status = task.DONE
270 self._queue = tuple(tasks)
278 return self._queue[0]
287 for child in self._queue: