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 print "mp3togo %s\n" % mp3togo.version
152 print "<space> or 'p' to pause, <esc> or 'q' to quit\n"
154 start_time = time.time()
161 fd = sys.stdin.fileno()
162 oldterm = termios.tcgetattr(fd)
163 newattr = termios.tcgetattr(fd)
164 newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO
165 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)
170 if opts['verbosity']:
171 tryp = conf.try_print
173 tryp = lambda x: None
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()
218 ts = util.format_time(tm)
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)
243 opts.log(1, "Out of space. Exiting.")
247 if trk() == track.DONE:
254 tt = time.time() - track_start
255 ts = util.format_time(tt)
257 tryp("Total time this track: %s\n\n" % ts)
261 tm = time.time() - start_time
262 ts = util.format_time(tm)
265 bad_str = ", 1 track failed."
267 bad_str = ", %d tracks failed." % bad_ones
270 #bytes = format_bytes(pl.produced_bytes) # you are using a new pool each time!
271 tryp("\n%d tracks done%s %s total time elapsed.\n" %
272 (good_ones, bad_str, ts))
276 termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm)
277 fcntl.fcntl(fd, fcntl.F_SETFL, oldflags)
280 if __name__ == "__main__": main(sys.argv)