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 print "mp3togo %s\n" % mp3togo.version
144 print "<space> or 'p' to pause, <esc> or 'q' to quit\n"
146 start_time = time.time()
153 fd = sys.stdin.fileno()
154 oldterm = termios.tcgetattr(fd)
155 newattr = termios.tcgetattr(fd)
156 newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO
157 termios.tcsetattr(fd, termios.TCSANOW, newattr)
159 oldflags = fcntl.fcntl(fd, fcntl.F_GETFL)
160 fcntl.fcntl(fd, fcntl.F_SETFL, oldflags | os.O_NONBLOCK)
162 if opts['verbosity']:
163 tryp = conf.try_print
165 tryp = lambda x: None
167 for name in playlist:
168 track_start = time.time()
169 tryp("(%d/%d) %s: \n" % (playlist.cur_file() + 1, len(playlist), name))
170 trk = track.Track(name, opts, cooked)
171 pl = pool.Pool(opts) # make a new one for every track? Huh?
172 if pl.add_track(trk):
177 if tsk.name in ('Start', 'Done', 'Cleaning'):
181 while tsk.status() == task.RUNNING:
186 bar = '#' * int(pcent/5.0) + ('_' * (20 - int(pcent/5.0)))
190 tryp("%s: %3.d%% %s" % (tsk.name, int(pcent), bar))
193 for busy in range(5):
195 c = sys.stdin.read(1)
198 if c in ('q', 'Q', chr(27)):
199 raise KeyboardInterrupt
200 elif c in (' ', 'p', 'P'):
202 tryp("\r [PAUSED] hit <enter> to resume ")
205 c = sys.stdin.read(1)
209 tm = tsk.elapsed_time()
213 if tsk.status() == task.DONE:
215 tryp("\r%s: Done. %s elapsed.\n" % (tsk.name, ts))
217 tryp("\r%s: Done.\n" % tsk.name)
220 tryp("\r%s: Failed. Cleaning up.\n" % tsk.name)
222 tryp("Undo: %s\n" % tsk.name)
225 except KeyboardInterrupt:
227 tryp("Stopping...\n")
229 tryp("Undo: %s\n" % tsk.name)
235 opts.log(1, "Out of space. Exiting.")
239 if trk() == track.DONE:
246 tt = time.time() - track_start
249 tryp("Total time this track: %s\n\n" % ts)
253 tm = time.time() - start_time
257 bad_str = ", 1 track failed."
259 bad_str = ", %d tracks failed." % bad_ones
262 #bytes = format_bytes(pl.produced_bytes) # you are using a new pool each time!
263 tryp("\n%d tracks done%s %s total time elapsed.\n" %
264 (good_ones, bad_str, ts))
268 termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm)
269 fcntl.fcntl(fd, fcntl.F_SETFL, oldflags)
275 hr, min, sec = (tm/3600, (tm%3600)/60, (tm%3600)%60)
278 ts = ("%dm " % min) + ts
280 ts = ("%dh " % hr) + ts
285 def format_bytes(bytes):
290 ret = str(int(bytes/k)) + " KB"
292 ret = str(int(bytes/k)) + " KB"
294 ret = str(int(bytes/m)) + " MB"
296 ret = str(bytes) + " Bytes"
299 if __name__ == "__main__": main(sys.argv)