4 # This file is part of mp3togo
6 # Convert audio files to play on a mp3 player
8 # (c) Simeon Veldstra 2004, 2006 <reallifesim@gmail.com>
10 # This software is free.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You may redistribute this program under the terms of the
18 # GNU General Public Licence version 2
19 # Available in this package or at http://www.fsf.org
22 """Convert a list of files.
24 Find a list of audio files in various
25 formats and qualities and convert them
26 to a uniform format suitable for a
36 import mp3togo.filelist as filelist
37 import mp3togo.conf as conf
38 import mp3togo.track as track
39 import mp3togo.task as task
40 import mp3togo.pool as pool
41 import mp3togo.cache as cache
42 import mp3togo.cluster as cluster
45 def fail(mesg='', code=1):
47 print >>sys.stderr, mesg
52 # Read in configuration
54 opts = conf.Options(argv)
55 except mp3togo.options.Fail, msg:
56 print conf.Options().usage()
59 # Cluster mode is handled in the cluster module
60 if opts['clusterslave']:
64 # Are we creating a cache?
66 if not opts['cachesize']:
67 opts.log(1, "Must specify a --cache-size to create a cache")
69 if cache.create(opts['makecache'], opts):
70 opts.log(1, "Cache successfully created at %s" % opts['makecache'])
73 opts.log(1, "Create cache at %s failed" % opts['makecache'])
76 # Are we using a cache?
79 cooked = cache.Cache(opts['usecache'], opts)
81 opts.log(1, "Error reading cache at %s" % opts['usecache'])
86 if not opts.bin[opts['encoder']]:
87 opts.log(1, "Encoder binary '%s' not found! Quiting." % opts['encoder'])
89 if not opts.bin['normalize-audio'] and not opts['nonormal']:
90 opts.log(1, "'normalize-audio' binary not found. Normalization disabled.")
91 opts['nonormal'] = True
92 if not os.path.isdir(opts['playerdir']):
93 opts.log(1, "target_dir does not exist")
97 playlist = filelist.FileList(opts)
99 # We could move these into FileList,
100 # but, we may want interactive import buttons in a GUI later.
102 playlist.addplaylist(opts['playlist'])
104 if opts['arg_files']:
105 playlist.addfiles(opts['arg_files'])
111 except conf.ErrorNoXMMSControl:
112 opts.log(1, "python-xmms is not installed. Can't open XMMS.")
113 except conf.ErrorXMMSNotRunning:
114 opts.log(1, "XMMS Does not seem to be running.")
116 if opts['readamarok']:
120 except conf.ErrorAmarokNotRunning:
121 opts.log(1, "Amarok Does not seem to be running.")
123 opts.log(1, "dcop is not installed. Can't open Amarok.")
126 if len(playlist) == 0:
128 opts.log(1, "No files to convert.")
133 cluster.slavedriver(playlist, opts, cooked)
137 execute_sequential(playlist, opts, cooked)
141 def execute_sequential(playlist, opts, cooked=None):
142 """Run the conversions one at a time"""
143 # shouldn't print if verbose is 0:
144 if opts['verbosity']:
145 tryp = conf.try_print
147 tryp = lambda x: None
149 tryp("mp3togo %s\n\n" % mp3togo.version)
150 tryp("<space> or 'p' to pause, <esc> or 'q' to quit\n\n")
152 start_time = time.time()
160 if opts['verbosity']:
162 fd = sys.stdin.fileno()
163 oldterm = termios.tcgetattr(fd)
164 newattr = termios.tcgetattr(fd)
165 newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO
166 termios.tcsetattr(fd, termios.TCSANOW, newattr)
167 oldflags = fcntl.fcntl(fd, fcntl.F_GETFL)
168 fcntl.fcntl(fd, fcntl.F_SETFL, oldflags | os.O_NONBLOCK)
171 fail('Error setting the terminal to raw mode.\nIs this a tty?')
174 pl = pool.Pool(opts) # make a new one for every track? Huh?
175 for name in playlist:
176 track_start = time.time()
177 tryp("(%d/%d) %s: \n" % (playlist.cur_file() + 1, len(playlist), name))
178 trk = track.Track(name, opts, cooked)
179 #pl = pool.Pool(opts) # make a new one for every track? Huh?
180 if pl.add_track(trk):
185 if tsk.name in ('Start', 'Done', 'Cleaning'):
189 while tsk.status() == task.RUNNING:
194 bar = '#' * int(pcent/5.0) + ('_' * (20 - int(pcent/5.0)))
198 tryp("%s: %3.d%% %s" % (tsk.name, int(pcent), bar))
201 for busy in range(5):
203 c = sys.stdin.read(1)
206 if c in ('q', 'Q', chr(27)):
207 raise KeyboardInterrupt
208 elif c in (' ', 'p', 'P'):
210 tryp("\r [PAUSED] hit <enter> to resume ")
213 c = sys.stdin.read(1)
217 tm = tsk.elapsed_time()
221 if tsk.status() == task.DONE:
223 tryp("\r%s: Done. %s elapsed.\n" % (tsk.name, ts))
225 tryp("\r%s: Done.\n" % tsk.name)
228 tryp("\r%s: Failed. Cleaning up.\n" % tsk.name)
230 tryp("Undo: %s\n" % tsk.name)
233 except KeyboardInterrupt:
235 tryp("Stopping...\n")
237 tryp("Undo: %s\n" % tsk.name)
242 opts.log(1, "Out of space. Exiting.")
246 if trk() == track.DONE:
253 tt = time.time() - track_start
256 tryp("Total time this track: %s\n\n" % ts)
260 tm = time.time() - start_time
264 bad_str = ", 1 track failed."
266 bad_str = ", %d tracks failed." % bad_ones
269 bytes = format_bytes(pl.produced_bytes)
270 tryp("\n%s written for %d tracks%s %s total time elapsed.\n" %
271 (bytes, good_ones, bad_str, ts))
276 termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm)
277 fcntl.fcntl(fd, fcntl.F_SETFL, oldflags)
283 hr, min, sec = (tm/3600, (tm%3600)/60, (tm%3600)%60)
286 ts = ("%dm " % min) + ts
288 ts = ("%dh " % hr) + ts
293 def format_bytes(bytes):
298 return "%.2f KB" % (bytes/k)
299 elif m <= bytes: # < g:
300 return "%.2f MB" % (bytes/m)
302 return str(bytes) + " Bytes"
305 if __name__ == "__main__": main(sys.argv)