]>
diplodocus.org Git - flac-archive/blob - fa-rip
1 #! /usr/bin/env python2.4
6 B<fa-rip> - rip a CD for B<fa-flacd>
10 B<fa-rip> [B<-d> I<device>] [B<-p> I<post-processor> [B<-t> I<track-count>]
14 B<fa-rip> creates a temporary directory for storage of its
15 intermediate files, uses MusicBrainz to create the "cue" file and
16 candidate tags files, and runs C<cdparanoia(1)> to rip the CD to the
19 In order for this CD to be processed by B<fa-flacd>, you must create a
20 "tags" file. This is usually done by renaming one of the
21 candidate-tags files and deleting the others. Don't forget to fill in
22 the DATE tag in the selected candidate before renaming it. If
23 B<fa-rip> could not find any tag information from MusicBrainz, you'll
24 have to fill out the candidate-tags-0 template.
30 =item B<-d> [B<--device>] I<device>
32 Use I<device> as the CD-ROM device, instead of the default
33 "/dev/cdrom" or the environment variable CDDEV.
35 =item B<-p> [B<--post-processor>] I<post-processor>
37 Create a "post-processor" file in the temporary directory containing
38 the line 'I<post-processor> "$@"'. See B<fa-flacd>'s man page for
39 information about this hook.
41 =item B<-t> [B<--tracks>] I<track-count>
43 Archive only the first I<track-count> tracks. This is handy for
54 B<fa-rip> uses this to rip audio and save the cuesheet for a CD.
55 MusicBrainz::Client can usually figure this out automatically.
61 Written by Eric Gillespie <epg@pretzelnet.org>.
63 flac-archive is free software; you may redistribute it and/or modify
64 it under the same terms as Perl itself.
68 ''' #' # python-mode is sucks
70 import os
, sys
, tempfile
, traceback
71 from optparse
import OptionParser
74 import musicbrainz2
.disc
75 import musicbrainz2
.webservice
77 from org
.diplodocus
.util
import catch_EnvironmentError
as c
79 def mkcue(disc
, trackcount
=None):
80 fp
= c(file, 'cue', 'w')
81 c(fp
.write
, 'FILE "dummy.wav" WAVE\n')
82 c(fp
.write
, ' TRACK 01 AUDIO\n')
83 c(fp
.write
, ' INDEX 01 00:00:00\n')
85 if trackcount
== None:
86 trackcount
= disc
.lastTrackNum
88 trackcount
= min(trackcount
, disc
.lastTrackNum
)
90 pregap
= disc
.tracks
[0][0]
91 for i
in xrange(disc
.firstTrackNum
, trackcount
):
92 offset
= disc
.tracks
[i
][0]
100 minutes
= seconds
/ 60
101 seconds
= seconds
% 60
103 c(fp
.write
, ' TRACK %02d AUDIO\n' % (i
+ 1,))
105 ' INDEX 01 %02d:%02d:%02d\n' % (minutes
, seconds
, sectors
))
111 def tags_file(fn
, trackcount
, various
, artist
=None, album
=None,
112 release_dates
={}, tracks
=[]):
113 fp
= c(file, fn
, 'w')
114 c(fp
.write
, 'ARTIST=')
117 c(fp
.write
, '\nALBUM=')
123 for (country
, date
) in release_dates
.items():
125 c(fp
.write
, 'DATE[%s]=%s\n' % (country
, date
))
126 have_date
or c(fp
.write
, 'DATE=\n')
128 for i
in xrange(1, trackcount
+ 1):
130 track
= tracks
.pop(0)
132 artist
= track
.artist
136 various
and c(fp
.write
, 'ARTIST[%d]=%s\n' % (i
, artist
))
137 c(fp
.write
, 'TITLE[%d]=%s\n' % (i
, title
))
141 def cover_art(i
, asin
):
142 url
= 'http://images.amazon.com/images/P/%s.01.MZZZZZZZ.jpg' % (asin
,)
143 fp
= file('cover.front-' + i
, 'w')
144 fp
.write(urllib
.urlopen(url
).read())
147 def tags(disc
, trackcount
, mb
=True):
151 tags_file('candidate-tags-0', trackcount
, False)
156 include
= musicbrainz2
.webservice
.ReleaseIncludes(tracks
=True)
157 q
= musicbrainz2
.webservice
.Query()
158 filter = musicbrainz2
.webservice
.ReleaseFilter(discId
=disc
.getId())
161 for album
in q
.getReleases(filter):
163 various
= not album
.release
.isSingleArtistRelease()
165 if various
and not seen_various
:
167 tags_file('candidate-tags-0v', trackcount
, True)
169 tags_file('candidate-tags-' + str(i
), trackcount
, various
,
170 album
.release
.artist
.name
, album
.release
.title
,
171 album
.release
.getReleaseEventsAsDict(),
172 q
.getReleaseById(album
.release
.id, include
).tracks
)
174 cover_art(str(i
), album
.release
.asin
)
176 def rip(device
, trackcount
, single_file
):
178 device
= '/dev/cdrom'
180 argv
= ['cdparanoia', '-d', device
, '1-' + str(trackcount
)]
187 c(os
.execvp
, argv
[0], argv
)
189 def make_post_processor(command
):
193 fd
= c(os
.open, 'post-processor', os
.O_CREAT | os
.O_WRONLY
, 0555)
194 fp
= c(os
.fdopen
, fd
, 'w')
195 c(fp
.write
, command
+' "$@"\n')
199 # Control the exit code for any uncaught exceptions.
201 parser
= OptionParser()
202 parser
.disable_interspersed_args()
203 parser
.add_option('-d', '--device')
204 parser
.add_option('-m', '--no-musicbrainz',
205 action
='store_true', default
=False)
206 parser
.add_option('-p', '--post-processor')
207 parser
.add_option('-s', '--single-file',
208 action
='store_true', default
=False)
209 parser
.add_option('-t', '--tracks', type='int', default
=99)
211 traceback
.print_exc()
215 # Raises SystemExit on invalid options in argv.
216 (options
, args
) = parser
.parse_args(argv
[1:])
217 except Exception, error
:
218 if isinstance(error
, SystemExit):
220 traceback
.print_exc()
224 device
= options
.device
225 trackcount
= options
.tracks
227 tempdir
= c((lambda x
: tempfile
.mkdtemp(prefix
=x
, dir='.')),
231 make_post_processor(options
.post_processor
)
233 disc
= musicbrainz2
.disc
.readDisc(device
)
235 trackcount
= mkcue(disc
, trackcount
)
236 tags(disc
, trackcount
, mb
=not options
.no_musicbrainz
)
237 rip(device
, trackcount
, options
.single_file
)
238 except Exception, error
:
239 if isinstance(error
, SystemExit):
241 # check all print_exc and format_exc in fa-flacd.py; i think
242 # for some i don't do this msg print check
243 sys
.stderr
.write(getattr(error
, 'msg', ''))
244 traceback
.print_exc()
249 if __name__
== '__main__':
250 sys
.exit(main(sys
.argv
))