]>
diplodocus.org Git - flac-archive/blob - flac2mp3
6 B<flac2mp3> - transcode FLAC file to MP3 files
10 B<flac2mp3> [B<--lame-options> I<lame-options>] [B<-j> I<jobs>] [B<-q>] [B<-v>] I<file> [...]
14 B<flac2mp3> transcodes the FLAC files I<file> to MP3 files. I<file>
15 may be the kind of FLAC file B<fa-flacd> generates. That is, it
16 contains a cue sheet, one TITLE tag per track listed therein, and
17 ARTIST, ALBUM, and DATE tags.
19 Note that lame is retarded, and parses B<LANG> directly itself! So, in order
20 for it to transcode textual tags, you must specify the encoding in LANG, e.g.
27 =item B<--lame-options> I<lame-options>
29 Pass I<lame-options> to B<lame>. This ends up being passed to the
30 shell, so feel free to take advantage of that. You'll almost
31 certainly have to put I<lame-options> in single quotes.
33 =item B<-j> [B<--jobs>] I<jobs>
35 Run up to I<jobs> jobs instead of the default 1.
37 =item B<-q> [B<--quiet>]
39 Suppress status information. This option is passed along to B<flac>
42 =item B<-v> [B<--verbose>]
44 Print diagnostic information. This option is passed along to B<flac>
51 Written by Eric Gillespie <epg@pretzelnet.org>.
57 import os
, re
, sys
, tempfile
, traceback
58 from optparse
import OptionParser
59 from subprocess
import Popen
, PIPE
61 import org
.diplodocus
.jobs
62 from org
.diplodocus
.util
import run_or_die
64 from flac_archive
.tags
import Tags
66 ################################################################################
69 def flac2mp3(fn
, title
, artist
, album_artist
, album
, discnum
, date
,
71 (title
, artist
, album
) = [(x
== None and 'unknown') or x
72 for x
in (title
, artist
, album
)]
77 flac_options
= '--silent'
83 if lame_options
!= None:
84 tmp
.append(lame_options
)
86 tmp
.append('--preset standard');
87 quiet
and tmp
.append('--quiet')
88 verbose
and tmp
.append('--verbose')
89 lame_options
= ' '.join(tmp
)
91 # Escape any single quotes ' so we can quote this.
92 (fn
, title
, artist
, album_artist
,
93 album
, date
) = [(x
or '').replace("'", r
"'\''")
94 for x
in [fn
, title
, artist
, album_artist
, album
, date
]]
96 album_artist_options
= ''
98 album_artist_options
= "--tv 'TPE2=%s'" % album_artist
100 outfile_album
= album
103 outfile_album
= '%s (disc %s)' % (album
, discnum
)
104 discnum_options
= "--tv 'TPOS=%d'" % int(discnum
)
106 quoted_outfile
= ('%s (%s) %02d %s.mp3' % (artist
, outfile_album
,
107 track
, title
)).replace('/', '_')
110 return quoted_outfile
.replace(r
"'\''", "'")
113 (fd
, picfn
) = tempfile
.mkstemp()
115 p
= Popen(['metaflac', '--export-picture-to=' + picfn
, fn
], stderr
=PIPE
)
117 stderr
= ''.join(p
.stderr
)
118 # Hacky check for flac with no album art
119 if 'no PICTURE block' in stderr
:
120 # That's fine, just no picture.
124 sys
.stderr
.write('metaflac exited %d: %s\n' % (status
, stderr
))
126 pic_options
= "--ti '%s'" % picfn
127 os
.system('md5sum ' + picfn
)
129 run_or_die(3, "flac %s -cd %s '%s' | lame --id3v2-only --id3v2-latin1 --pad-id3v2-size 0 %s --tt '%s' --ta '%s' --tl '%s' --ty '%s' --tn %d %s %s %s - '%s'"
130 % (flac_options
, ' '.join(skip_until
), fn
,
131 lame_options
, title
, artist
, album
, date
, track
,
132 pic_options
, album_artist_options
,
133 discnum_options
, quoted_outfile
))
143 ################################################################################
146 def tformat(m
, s
, c
):
147 return '%02d:%02d.%02d' % (m
, s
, c
)
149 def get_decode_args(fn
):
152 p
= Popen(['metaflac', '--no-utf8-convert', '--export-cuesheet-to=-', fn
],
154 for line
in (x
.rstrip() for x
in p
.stdout
):
155 m
= re
.search(r
'INDEX 01 (\d\d):(\d\d):(\d\d)$', line
)
157 l
.append(map(int, m
.groups()))
159 # XXX dataloss! check status
162 for i
in xrange(len(l
)):
163 arg
= ['--skip=' + tformat(*l
[i
])]
171 arg
.append('--until=' + tformat(next
[0] - 1, 59, 74))
173 arg
.append('--until=' + tformat(next
[0], next
[1] - 1,
176 arg
.append('--until=' + tformat(next
[0], next
[1],
181 # If no cue sheet, stick a dummy in here.
188 """Return the ARTIST, ALBUM, and DATE tags followed by the TITLE tags
193 p
= Popen(['metaflac', '--no-utf8-convert', '--export-tags-to=-', fn
],
197 # XXX dataloss! check status
203 # Control the exit code for any uncaught exceptions.
205 parser
= OptionParser()
206 parser
.disable_interspersed_args()
207 parser
.add_option('-X', '--debug', action
='store_true', default
=False)
208 parser
.add_option('-j', '--jobs', type='int', default
=1)
209 parser
.add_option('--lame-options')
210 parser
.add_option('-q', '--quiet', action
='store_true', default
=False)
211 parser
.add_option('-v', '--verbose', action
='store_true', default
=False)
212 parser
.add_option('--check-missing-files', action
='store_true',
215 traceback
.print_exc()
219 # Raises SystemExit on invalid options in argv.
220 (options
, args
) = parser
.parse_args(argv
[1:])
221 except Exception, error
:
222 if isinstance(error
, SystemExit):
224 traceback
.print_exc()
229 global debug
, flac_options
, lame_options
, quiet
, verbose
231 check_missing
= options
.check_missing_files
232 debug
= options
.debug
233 lame_options
= options
.lame_options
234 quiet
= options
.quiet
235 verbose
= options
.verbose
240 args
= get_decode_args(fn
)
243 album
= tags
.gets('ALBUM', separator
=separator
)
244 discnum
= tags
.gets('DISCNUMBER')
245 track
= tags
.gets('TRACKNUMBER')
247 # Stupid hack: only a single-track file should have the
248 # TRACKNUMBER tag, so use it if set for the first pass through
249 # the loop. At the end of the loop, we'll set $track for the
250 # next run, so this continues to work for multi-track files.
256 for i
in range(len(tags
)):
257 title
= tags
.gets('TITLE', track
, separator
)
258 part
= tags
.gets('PART', track
)
260 title
= '%s - %s' % (title
, part
)
261 version
= tags
.gets('VERSION', track
)
263 title
= '%s (%s)' % (title
, version
)
264 artist
= tags
.get('ARTIST', track
)
265 artist
.extend(tags
.get('FEATURING', track
))
266 album_artist
= tags
.gets('ALBUMARTIST', track
)
268 mp3
= flac2mp3(fn
, title
,
270 album_artist
, album
, discnum
,
271 tags
.gets('DATE', track
),
273 if not os
.path
.exists(mp3
):
277 jobs
.append((fn
, title
,
279 album_artist
, album
, discnum
,
280 tags
.gets('DATE', track
),
283 except Exception, error
:
284 sys
.stderr
.write(getattr(error
, 'msg', ''))
285 traceback
.print_exc()
286 sys
.stderr
.write('Continuing...\n')
293 return lambda: flac2mp3(*job
)
294 org
.diplodocus
.jobs
.run(maxjobs
=options
.jobs
, debug
=debug
, get_job
=getjob
)
295 except Exception, error
:
296 if isinstance(error
, SystemExit):
298 # check all print_exc and format_exc in fa-flacd.py; i think
299 # for some i don't do this msg print check
300 sys
.stderr
.write(getattr(error
, 'msg', ''))
301 traceback
.print_exc()
306 if __name__
== '__main__':
307 sys
.exit(main(sys
.argv
))