mp3togo version 0.5.2
mp3togo/conf.py
1 # - conf.py -
2 # This file is part of mp3togo
3
4 # Convert audio files to play on a mp3 player
5 # Manage program options
6 #
7 # (c) Simeon Veldstra 2004, 2006 <reallifesim@gmail.com>
8 #
9 # This software is free.
10 #
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.
15 #
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
19
20 # requires python-pyvorbis and python-id3 and lame
21 # and mpg321
22
23 import sys, os
24
25 import mp3togo
26 import mp3togo.options as options
27
28
29 class Fail(options.Fail):
30 pass
31
32 class Error(Exception):
33 pass
34
35 class ErrorUnknownFileType(Error):
36 pass
37
38 class ErrorNoFile(Error):
39 pass
40
41 class ErrorNoCache(Error):
42 pass
43
44 class ErrorBadFormat(Error):
45 pass
46
47 class ErrorNoXMMSControl(Error):
48 pass
49
50 class ErrorXMMSNotRunning(Error):
51 pass
52
53 class ErrorNoDCOP(Error):
54 pass
55
56 class ErrorAmarokNotRunning(Error):
57 pass
58
59 class ErrorUnlocked(Error):
60 pass
61
62 class TaskNotReadyError(Error):
63 pass
64
65 class ErrorClusterProtocol(Error):
66 pass
67
68
69
70 class Options(options.Options):
71 """Subclass options.Options and fill in application specific
72 information."""
73 def __init__(self, argv=None, conffile=None, readconf=True):
74 options.Options.__init__(self, '~/mp3togo')
75
76 # Options to add:
77 # ((name, short option, long option, default value, hook, help text), ...)
78 self._set += [
79 ('brwarning', 't', 'file-tag', '.2go', None,
80 '''A string appended to the name of the file to indicate it has been recoded.'''),
81 ('tempdir', 'w', 'work-dir', '/tmp', self._absfile,
82 '''The path to store temporary files. This could need several hundred megabytes free.'''),
83 ('treedepth', 'd', 'tree-depth', 1, None,
84 '''The number of directory levels to preserve in the output tree. Use 0 to put all output files directly into the target directory. Use 2 to create a directory for the Artist and then the Album (Assuming your music collection is organized in directories by artist then album).'''),
85 ('treestructure', '', 'format', '', None,
86 '''A string specifying the format for the output files. The format string can contain the following escapes:<li>%a - Artist<li>%l - Album<li>%t - Title<li>%y - Year<li>%g - Genre<li>%% - Literal '%'<br>Overrides --tree-depth.'''),
87 ('notags', '', 'no-tags', False, None,
88 '''Do not try to write tags to the output files.'''),
89 ('maxunits', 'm', 'max-size', '0', self._units,
90 '''The disk space available to use in bytes. Append 'M' for megabytes, 'G' for gigabytes or 'K' for kilobytes. Use 0 to use all available space on the filesystem.'''),
91 ('maxsize', '', '', 0L, None, ''),
92 ('maxtempunits', '', 'max-temp-size', '0', self._units,
93 '''The disk space available to use for temporary files in bytes. Append 'M' for megabytes, 'G' for gigabytes or 'K' for kilobytes. Use 0 to use all available space on the filesystem.'''),
94 ('maxtempsize', '', '', 0L, None, ''),
95 ('force', 'F', 'force', False, None,
96 '''Overwrite files if they already exist.'''),
97 ('encoder', 'C', 'encoder', 'lame', None,
98 '''The encoder to use to create the output files. Options are wav, lame or oggenc.'''),
99 ('encopts', 'E', 'encoder-options', '', None,
100 '''Compression options for the encoder.'''),
101 ('compfactor', 'z', 'compression-factor', 16.0, None,
102 '''If you change the lame options, you must change this factor to match the compression rate of the new options. This is used to estimate completed file size.'''),
103 ('makecache', '', 'make-cache', '', self._absfile, '''Make an output file cache at the given path.'''),
104 ('cachesize', '', '', 0L, None, ''),
105 ('cacheunits', '', 'cache-size', '0', self._units, '''The size for the new cache.'''),
106 ('usecache', '', 'use-cache', '', self._absfile, '''Use the cache stored at the given path.'''),
107 ('help', 'h', 'help', False, None, '''Print this message.'''),
108 ('playlist', 'p', 'playlist', '', None,
109 '''The playlist to convert. Playlists can be simple lists of file paths, one per line, or .m3u files or the native XMMS playlist file format. Playlists can be also listed on the command line as free arguments after all of the options if they have a recognizable extension. Currently .m3u and .pls are recognized.'''),
110 ('readxmms', 'x', 'import-xmms', False, None,
111 '''Get playlist from running instance of XMMS. (You must have the python-xmms module installed)'''),
112 ('readamarok', '', 'import-amarok', False, None,
113 '''Get playlist from running instance of Amarok.'''),
114 ('playerdir', 'o', 'output-dir', os.curdir, self._absfile,
115 '''Where to write the output files.'''),
116 ('index', '', 'index', False, None,
117 '''Create an index file when in wav output mode.'''),
118 ('nonormal', '', 'no-normalize', False, None,
119 '''Don't normalize the wav file before encoding.'''),
120 ('cluster', '', 'cluster', '', None,
121 '''Manage a cluster of worker machines. Cluster mode distributes tracks between multiple computers connected to a LAN. Provide a comma separated list of IP addresses or hostnames. The master machine must be able to log in to the slaves without a password and the slaves must have shared access to the filesystem. See the website for an example of how to set it up.<li>http://puddle.ca/mp3togo/'''),
122 ('clusternolocal', '', 'cluster-no-local-node', False, None,
123 '''Only use the remote nodes for processing files.'''),
124 ('clusterslave', '', 'cluster-slave', False, None,
125 '''Start a worker process for cluster mode. This option is used by the cluster master to start slave processes. Do not use.''')]
126
127 # Override hook defined in Base class
128 self._conffile = self._absfile
129
130 # Options to not save to the config file:
131 self._nosave_options += []
132
133 # Binaries to check for:
134 self.bin['wav'] = True
135 self.bin['lame'] = False
136 self.bin['oggenc'] = False
137 self.bin['mpg321'] = False
138 self.bin['ogg123'] = False
139 self.bin['flac'] = False
140 self.bin['faad'] = False
141 self.bin['metaflac'] = False
142 self.bin['normalize-audio'] = False
143 self.bin['dcop'] = False
144
145 self.version = mp3togo.version
146 self._help_preamble = """
147 Synopsis:
148 mp3togo [-p playlist] [-o output-dir] [options] [input files]
149
150 Description:
151 mp3togo is a program for converting audio files of various
152 formats into a common format suitable for use on a portable
153 mp3 player or an mp3 CD. The files to convert can be
154 specified in a playlist file (m3u, pls and plain text are
155 supported) or given as arguments to the program. mp3togo
156 will go through the list and run mpg321, ogg123, flac, faad and
157 lame to decode and reencode the files. The newly encoded
158 files will be written to the destination directory. The
159 software can retain as much of the subdirectory structure
160 as desired.
161 """
162
163 # Check for Third party modules:
164 self.mod['ogg.vorbis'] = False
165 self.mod['ID3'] = False
166 self.mod['xmms'] = False
167
168 # Go ahead and read in the data:
169 self.getconf(argv, conffile, readconf)
170
171
172
173 # All hooks are called with the lock held
174 # and must reference the ._d dict directly
175 def _post_hook(self):
176 #Set up default encoder options if not specified:
177 if not self._d['encopts']:
178 self.reset_encoder_options()
179
180 def _absfile(self, name, value):
181 self._d[name] = absfile(value)
182
183 def _units(self, name, value):
184 #Calculate raw bytes and set [max|temp|cache]size
185 try:
186 if value[-1] in ('m', 'M'):
187 max = long(value[:-1]) * 1048576L
188 elif value[-1] in ('g', 'G'):
189 max = long(value[:-1]) * 1073741824L
190 elif value[-1] in ('k', 'K'):
191 max = long(value[:-1]) * 1024L
192 else:
193 max = long(value)
194 except ValueError:
195 raise Fail, "Bad %s option: %s " % (name, value)
196 sizename = name.replace('units', 'size')
197 self._d[sizename] = max # 'maxsize'
198 self._d[name] = value # 'maxunits'
199
200 def _arg_files(self, name, value):
201 l = []
202 for f in value:
203 f = absfile(f)
204 if os.path.exists(f):
205 l.append(f)
206 self._d[name] = l
207
208 def reset_encoder_options(self):
209 """Reset encoder options to defaults."""
210 if self._d['encoder'] == 'lame':
211 self._d['encopts'] = '--abr 96'
212 elif self._d['encoder'] == 'oggenc':
213 self._d['encopts'] = '-m 96 -M 225 -b 100'
214 elif self._d['encoder'] == 'wav':
215 self._d['encopts'] = ''
216
217 def cleanfilename(self, name):
218 """Remove nasty characters from a filename"""
219 name = name.replace('"', "'")
220 name = name.replace(':', '.')
221 name = name.replace("?", "")
222 name = name.replace('*', '')
223 name = name.replace('`', "'")
224 name = name.replace('&', '+')
225 name = name.replace(';', '.')
226 name = name.replace('#', '-')
227 if name[0] == '.':
228 name = '_' + name
229 return name
230
231 def absfile(name):
232 name = name.strip()
233 name = os.path.expanduser(name)
234 if name:
235 name = os.path.abspath(name)
236 return name
237
238 def getfiletype(filename):
239 """Return the format of an audio file"""
240 # Could use some more magic here
241 ft = os.path.splitext(filename)[1][1:]
242 if ft not in ('mp3', 'ogg', 'flac', 'wav', 'm4a'):
243 raise ErrorUnknownFileType
244 return ft
245
246 def checkfile(name):
247 """Verify the existence of an audio file"""
248 name = name.replace('\n', '')
249 if not os.path.exists(name):
250 raise ErrorNoFile
251 name = absfile(name)
252 getfiletype(name) # Throws exception if unknown
253 return name
254
255 def try_print(msg):
256 """Try to print a message to a nonblocking stdout"""
257 for i in range(5):
258 try:
259 sys.stdout.write(msg)
260 return
261 except:
262 time.sleep(0.01 ** (1.0 / i))
263