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.util as util
37 import mp3togo.filelist as filelist
38 import mp3togo.conf as conf
39 import mp3togo.track as track
40 import mp3togo.task as task
41 import mp3togo.pool as pool
42 import mp3togo.cache as cache
43 import mp3togo.cluster as cluster
45 import mp3togo.gui.mainwindow as mainwindow
48 def fail(mesg='', code=1):
50 print >>sys.stderr, mesg
55 # Read in configuration
57 opts = conf.Options(argv)
58 except mp3togo.options.Fail, msg:
59 print conf.Options().usage()
62 # Hand off control for gui interface
64 gui = mainwindow.start_gui(opts)
67 # Cluster mode is handled in the cluster module
68 if opts['clusterslave']:
72 # Are we creating a cache?
74 if not opts['cachesize']:
75 opts.log(1, "Must specify a --cache-size to create a cache")
77 if cache.create(opts['makecache'], opts):
78 opts.log(1, "Cache successfully created at %s" % opts['makecache'])
81 opts.log(1, "Create cache at %s failed" % opts['makecache'])
84 # Are we using a cache?
87 cooked = cache.Cache(opts['usecache'], opts)
89 opts.log(1, "Error reading cache at %s" % opts['usecache'])
94 if not opts.bin[opts['encoder']]:
95 opts.log(1, "Encoder binary '%s' not found! Quiting." % opts['encoder'])
97 if not opts.bin['normalize-audio'] and not opts['nonormal']:
98 opts.log(1, "'normalize-audio' binary not found. Normalization disabled.")
99 opts['nonormal'] = True
100 if not os.path.isdir(opts['playerdir']):
101 opts.log(1, "target_dir does not exist")
105 playlist = filelist.FileList(opts)
107 # We could move these into FileList,
108 # but, we may want interactive import buttons in a GUI later.
110 playlist.addplaylist(opts['playlist'])
112 if opts['arg_files']:
113 playlist.addfiles(opts['arg_files'])
119 except conf.ErrorNoXMMSControl:
120 opts.log(1, "python-xmms is not installed. Can't open XMMS.")
121 except conf.ErrorXMMSNotRunning:
122 opts.log(1, "XMMS Does not seem to be running.")
124 if opts['readamarok']:
128 except conf.ErrorAmarokNotRunning:
129 opts.log(1, "Amarok Does not seem to be running.")
131 opts.log(1, "dcop is not installed. Can't open Amarok.")
134 if len(playlist) == 0:
136 opts.log(1, "No files to convert.")
141 cluster.slavedriver(playlist, opts, cooked)
145 execute_sequential(playlist, opts, cooked)
149 def execute_sequential(playlist, opts, cooked=None):
150 """Run the conversions one at a time"""
151 # shouldn't print if verbose is 0:
152 if opts['verbosity']:
153 tryp = conf.try_print
155 tryp = lambda x: None
157 tryp("mp3togo %s\n\n" % mp3togo.version)
158 tryp("<space> or 'p' to pause, <esc> or 'q' to quit\n\n")
160 start_time = time.time()
168 if opts['verbosity']:
170 fd = sys.stdin.fileno()
171 oldterm = termios.tcgetattr(fd)
172 newattr = termios.tcgetattr(fd)
173 newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO
174 termios.tcsetattr(fd, termios.TCSANOW, newattr)
175 oldflags = fcntl.fcntl(fd, fcntl.F_GETFL)
176 fcntl.fcntl(fd, fcntl.F_SETFL, oldflags | os.O_NONBLOCK)
179 fail('Error setting the terminal to raw mode.\nIs this a tty?')
182 pl = pool.Pool(opts) # make a new one for every track? Huh?
183 for name in playlist:
184 track_start = time.time()
185 tryp("(%d/%d) %s: \n" % (playlist.cur_file() + 1, len(playlist), name))
186 trk = track.Track(name, opts, cooked)
187 #pl = pool.Pool(opts) # make a new one for every track? Huh?
188 if pl.add_track(trk):
193 if tsk.name in ('Start', 'Done', 'Cleaning'):
197 while tsk.status() == task.RUNNING:
202 bar = '#' * int(pcent/5.0) + ('_' * (20 - int(pcent/5.0)))
206 tryp("%s: %3.d%% %s" % (tsk.name, int(pcent), bar))
209 for busy in range(5):
211 c = sys.stdin.read(1)
214 if c in ('q', 'Q', chr(27)):
215 raise KeyboardInterrupt
216 elif c in (' ', 'p', 'P'):
218 tryp("\r [PAUSED] hit <enter> to resume ")
221 c = sys.stdin.read(1)
225 tm = tsk.elapsed_time()
226 ts = util.format_time(tm)
229 if tsk.status() == task.DONE:
231 tryp("\r%s: Done. %s elapsed.\n" % (tsk.name, ts))
233 tryp("\r%s: Done.\n" % tsk.name)
236 tryp("\r%s: Failed. Cleaning up.\n" % tsk.name)
238 tryp("Undo: %s\n" % tsk.name)
241 except KeyboardInterrupt:
243 tryp("Stopping...\n")
245 tryp("Undo: %s\n" % tsk.name)
250 opts.log(1, "Out of space. Exiting.")
254 if trk() == track.DONE:
261 tt = time.time() - track_start
262 ts = util.format_time(tt)
264 tryp("Total time this track: %s\n\n" % ts)
268 tm = time.time() - start_time
269 ts = util.format_time(tm)
272 bad_str = ", 1 track failed."
274 bad_str = ", %d tracks failed." % bad_ones
277 bytes = format_bytes(pl.produced_bytes)
278 tryp("\n%s written for %d tracks%s %s total time elapsed.\n" %
279 (bytes, good_ones, bad_str, ts))
284 termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm)
285 fcntl.fcntl(fd, fcntl.F_SETFL, oldflags)
291 hr, min, sec = (tm/3600, (tm%3600)/60, (tm%3600)%60)
294 ts = ("%dm " % min) + ts
296 ts = ("%dh " % hr) + ts
301 def format_bytes(bytes):
306 return "%.2f KB" % (bytes/k)
307 elif m <= bytes: # < g:
308 return "%.2f MB" % (bytes/m)
310 return str(bytes) + " Bytes"
313 if __name__ == "__main__": main(sys.argv)