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 setup
26 import mp3togo.tags as tags
27 import mp3togo.task as task
28 import mp3togo.cache as cache
29 import mp3togo.helpers
31 helpers = mp3togo.helpers.helpers
45 """Encapsulate the transformation of a file."""
47 def __init__(self, filename, opts, cooked=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
85 self.tags = tags.Tags(filename)
89 filetype = setup.getfiletype(filename)
90 dir, base = os.path.split(filename)
91 base = os.path.splitext(base)[0]
92 base = base + opts['brwarning']
93 base = opts.cleanfilename(base)
94 if opts['treestructure'] != '':
95 # Standard format string
96 # See tags.Tags.format.__doc__
97 fmt = opts['treestructure']
98 dest = self.tags.format(fmt)
100 self._outdir = os.path.dirname(dest)
101 self._outdir = os.path.join(opts['playerdir'], self._outdir)
102 self._outname = os.path.join(opts['playerdir'], dest)
104 if opts['treedepth']:
105 head, dir = os.path.split(dir)
106 for i in range(opts['treedepth'] -1):
107 head, tail = os.path.split(head)
108 dir = os.path.join(tail, dir)
109 dir = opts.cleanfilename(dir)
112 self._outdir = os.path.join(opts['playerdir'], dir)
113 self._outname = os.path.join(self._outdir, base)
114 self._outname += '.' + helpers[opts['encoder']]['type']
115 self._wavname = os.path.join(opts['tempdir'], base) + '.wav'
118 if not os.path.isdir(self._outdir):
119 os.makedirs(self._outdir)
123 if opts['treedepth']:
125 for i in range(opts['treedepth']):
130 tail, head = os.path.split(tail)
133 job = task.SimpleTask(self, make_dirs, None,
134 rm_dirs, name="Creating dirs")
138 # Check the cache - if there is one
140 self._hash = self._cache.search(self._filename)
143 return self._cache.recover(self._hash, self._outname)
144 def undo_recover_cache():
145 if os.path.exists(self._outname):
146 os.unlink(self._outname)
148 outreq = os.stat(self._cache.file(self._hash)).st_size
149 job = task.SimpleTask(self, recover_cache, None,
151 outsize=outreq, name="Hitting Cache")
157 prog = mp3togo.helpers.find_helper(filetype, 'decode')
158 tmpreq = mp3togo.helpers.est_decoded_size(self._filename)
159 jobname = "Decoding %s" % filetype
160 if callable(helpers[prog]['cmd']):
162 func = helpers[prog]['cmd'](self._filename, self._wavname)
163 job = task.SimpleTask(self, func, None, undo_decode,
164 tmpsize=tmpreq, name=jobname)
167 args = mp3togo.helpers.make_args(prog, self._filename,
169 job = task.Task(self, args, helpers[prog]['parser'],
170 undo_decode, tmpsize=tmpreq, name=jobname)
176 if opts.bin['normalize-audio']:
177 ncmd = [opts.bin['normalize-audio'], self._wavname]
178 job = task.Task(self, ncmd, lambda: '',
179 lambda: True, name="Normalizing")
183 if not opts['nonormal']:
184 opts.log(2, "'normalize-audio' binary not found, skipping.")
188 encoder = opts['encoder']
189 prog = helpers[encoder]
190 outreq = tmpreq / opts['compfactor']
191 jobname = "Encoding %s" % prog['type']
192 filter = prog['parser']
193 if callable(prog['cmd']):
194 func = prog['cmd'](self._wavname, self._outname)
195 job = task.SimpleTask(self, func, None, undo_encode,
196 outsize=outreq, name=jobname)
199 args = mp3togo.helpers.make_args(encoder, self._wavname,
200 self._outname, opts['encopts'])
201 job = task.Task(self, args, filter,
202 undo_encode, outsize=outreq, name=jobname)
208 job = task.SimpleTask(self, undo_decode, None, undo_decode,
216 indexname = os.path.join(self._outdir, '2go.index')
218 tags.remove_from_index(self._outname, indexname)
221 self.tags.writeindex(self._outname, indexname)
223 job = task.SimpleTask(self, write_index, None,
224 undo_index, name='Writing index')
229 # tag files from the cache as well, brwarning may have changed
230 if not opts['notags']:
232 self.tags.write(self._outname)
234 job = task.SimpleTask(self, tag_output, None,
235 lambda: True, name="Tagging")
239 # Cache the output if we had to go to the trouble of producing it
240 if not self._hash and self._cache:
241 self._hash = cache.checksum(self._filename)
243 self._cache.stash(self._outname, self._hash)
245 job = task.SimpleTask(self, cache_final, name="Caching result")
249 # Completion sentinel
250 job = task.SimpleTask(self, finish, None, start, name="Done")
254 # Consider the track done if the output file exists:
255 if os.path.exists(self._outname) and not opts['force']:
256 opts.log(1, "Skipping existing file: %s\n" % self._outname)
257 self._queue = tuple(tasks)
258 if self._hash and self._cache:
259 self._cache.release(self._hash)
260 for tsk in self._queue:
261 tsk._status = task.DONE
266 self._queue = tuple(tasks)
274 return self._queue[0]
283 for child in self._queue: