def do(self, args): runner = task.SyncRunner() t = cdparanoia.AnalyzeTask(self.options.device) runner.run(t) if t.defeatsCache is None: self.stdout.write( 'Cannot analyze the drive. Is there a CD in it?\n') return if not t.defeatsCache: self.stdout.write( 'cdparanoia cannot defeat the audio cache on this drive.\n') else: self.stdout.write( 'cdparanoia can defeat the audio cache on this drive.\n') info = drive.getDeviceInfo(self.options.device) if not info: self.stdout.write( 'Drive caching behaviour not saved: could not get device info (requires pycdio).\n' ) return self.stdout.write( 'Adding drive cache behaviour to configuration file.\n') self.getRootCommand().config.setDefeatsCache(info[0], info[1], info[2], t.defeatsCache)
def _testSuffix(self, suffix): # because of https://bugzilla.gnome.org/show_bug.cgi?id=688625 # we first create the file with a 'normal' filename, then rename self.runner = task.SyncRunner(verbose=False) fd, path = tempfile.mkstemp() cmd = "gst-launch " \ "audiotestsrc num-buffers=100 samplesperbuffer=1024 ! " \ "audioconvert ! audio/x-raw-int,width=16,depth=16,channels =2 ! " \ "wavenc ! " \ "filesink location=\"%s\" > /dev/null 2>&1" % ( gstreamer.quoteParse(path).encode('utf-8'), ) self.debug('Running cmd %r' % cmd) os.system(cmd) self.failUnless(os.path.exists(path)) os.close(fd) fd, newpath = tempfile.mkstemp(suffix=suffix) os.rename(path, newpath) encodetask = encode.EncodeTask(newpath, newpath + '.out', encode.WavProfile()) self.runner.run(encodetask, verbose=False) os.close(fd) os.unlink(newpath) os.unlink(newpath + '.out')
def testWrite(self): fd, inpath = tempfile.mkstemp(suffix=u'.morituri.tagwrite.flac') # wave is pink-noise because a pure sine is encoded too efficiently # by flacenc and triggers not enough frames in parsing # FIXME: file a bug for this in GStreamer os.system('gst-launch ' 'audiotestsrc ' 'wave=pink-noise num-buffers=10 samplesperbuffer=588 ! ' 'audioconvert ! ' 'audio/x-raw-int,channels=2,width=16,height=16,rate=44100 ! ' 'flacenc ! filesink location=%s > /dev/null 2>&1' % inpath) os.close(fd) fd, outpath = tempfile.mkstemp(suffix=u'.morituri.tagwrite.flac') self.runner = task.SyncRunner(verbose=False) taglist = gst.TagList() taglist[gst.TAG_ARTIST] = 'Artist' taglist[gst.TAG_TITLE] = 'Title' t = encode.TagWriteTask(inpath, outpath, taglist) self.runner.run(t) t = encode.TagReadTask(outpath) self.runner.run(t) self.failUnless(t.taglist) self.assertEquals(t.taglist['audio-codec'], 'FLAC') self.assertEquals(t.taglist['description'], 'audiotest wave') self.assertEquals(t.taglist[gst.TAG_ARTIST], 'Artist') self.assertEquals(t.taglist[gst.TAG_TITLE], 'Title') os.unlink(inpath) os.unlink(outpath)
def do(self, args): prog = program.Program() runner = task.SyncRunner() for arg in args: print 'Retagging image %r' % arg arg = arg.decode('utf-8') cueImage = image.Image(arg) cueImage.setup(runner) mbdiscid = cueImage.table.getMusicBrainzDiscId() prog.metadata = prog.getMusicBrainz(cueImage.table, mbdiscid) if not prog.metadata: print 'Not in MusicBrainz database, skipping' continue # FIXME: this feels like we're poking at internals. prog.cuePath = arg prog.result = result.RipResult() for track in cueImage.table.tracks: path = track.indexes[1].path taglist = prog.getTagList(track.number) self.debug('possibly retagging %r with taglist %r', path, taglist) t = encode.SafeRetagTask(path, taglist) runner.run(t) path = os.path.basename(path) if t.changed: print 'Retagged %s' % path else: print '%s already tagged correctly' % path print
def testRead(self): path = os.path.join(os.path.dirname(__file__), u'track.flac') self.runner = task.SyncRunner(verbose=False) t = encode.TagReadTask(path) self.runner.run(t) self.failUnless(t.taglist) self.assertEquals(t.taglist['audio-codec'], 'FLAC') self.assertEquals(t.taglist['description'], 'audiotest wave')
def testDefeatsCache(self): self.runner = task.SyncRunner(verbose=False) path = os.path.join(os.path.dirname(__file__), 'cdparanoia', 'PX-L890SA.cdparanoia-A.stderr') t = AnalyzeFileTask(path) self.runner.run(t) self.failUnless(t.defeatsCache)
def setUp(self): self._fd, self._path = tempfile.mkstemp(suffix=u'.morituri.retag.flac') os.system('gst-launch ' 'audiotestsrc ' 'num-buffers=40 samplesperbuffer=588 wave=pink-noise ! ' 'audioconvert ! ' 'audio/x-raw-int,channels=2,width=16,height=16,rate=44100 ! ' 'flacenc ! filesink location=%s > /dev/null 2>&1' % self._path) os.close(self._fd) self.runner = task.SyncRunner(verbose=False)
def do(self, args): # here to avoid import gst eating our options from morituri.common import encode prog = program.Program(self.getRootCommand().config, stdout=self.stdout) runner = task.SyncRunner() for arg in args: self.stdout.write('Retagging image %r\n' % arg) arg = arg.decode('utf-8') cueImage = image.Image(arg) cueImage.setup(runner) mbdiscid = cueImage.table.getMusicBrainzDiscId() self.stdout.write('MusicBrainz disc id is %s\n' % mbdiscid) self.stdout.write("MusicBrainz lookup URL %s\n" % cueImage.table.getMusicBrainzSubmitURL()) prog.metadata = prog.getMusicBrainz( cueImage.table, mbdiscid, release=self.options.release_id, country=self.options.country, prompt=self.options.prompt) if not prog.metadata: print 'Not in MusicBrainz database, skipping' continue prog.metadata.discid = mbdiscid # FIXME: this feels like we're poking at internals. prog.cuePath = arg prog.result = result.RipResult() for track in cueImage.table.tracks: path = cueImage.getRealPath(track.indexes[1].path) taglist = prog.getTagList(track.number) self.debug( 'possibly retagging %r from cue path %r with taglist %r', path, arg, taglist) t = encode.SafeRetagTask(path, taglist) runner.run(t) path = os.path.basename(path) if t.changed: print 'Retagged %s' % path else: print '%s already tagged correctly' % path print
def _testSuffix(self, suffix): self.runner = task.SyncRunner(verbose=False) fd, path = tempfile.mkstemp(suffix=suffix) t = image.AudioLengthTask(path) e = self.assertRaises(task.TaskException, self.runner.run, t, verbose=False) self.failUnless( isinstance(e.exception, gstreamer.GstException), "%r is not a gstreamer.GstException" % e.exceptionMessage) self.assertEquals(e.exception.gerror.domain, gst.STREAM_ERROR) # our empty file triggers TYPE_NOT_FOUND self.assertEquals(e.exception.gerror.code, gst.STREAM_ERROR_TYPE_NOT_FOUND) os.unlink(path)
def do(self, args): prog = program.Program(self.getRootCommand().config) prog.outdir = (self.options.output_directory or os.getcwd()) prog.outdir = prog.outdir.decode('utf-8') # here to avoid import gst eating our options from morituri.common import encode profile = encode.ALL_PROFILES[self.options.profile]() runner = task.SyncRunner() for arg in args: arg = arg.decode('utf-8') indir = os.path.dirname(arg) cueImage = image.Image(arg) cueImage.setup(runner) # FIXME: find a decent way to get an album-specific outdir root = os.path.basename(indir) outdir = os.path.join(prog.outdir, root) try: os.makedirs(outdir) except: # FIXME: handle other exceptions than OSError Errno 17 pass # FIXME: handle this nicer assert outdir != indir taskk = image.ImageEncodeTask(cueImage, profile, outdir) runner.run(taskk) # FIXME: translate .m3u file if it exists root, ext = os.path.splitext(arg) m3upath = root + '.m3u' if os.path.exists(m3upath): self.debug('translating .m3u file') inm3u = open(m3upath) outm3u = open(os.path.join(outdir, os.path.basename(m3upath)), 'w') for line in inm3u.readlines(): root, ext = os.path.splitext(line) if ext: # newline is swallowed by splitext here outm3u.write('%s.%s\n' % (root, profile.extension)) else: outm3u.write('%s' % root) outm3u.close()
def do(self, args): prog = program.Program() runner = task.SyncRunner() device = self.options.device # if necessary, load and unmount self.stdout.write('Checking device %s\n' % device) prog.loadDevice(device) prog.unmountDevice(device) # first get the Table Of Contents of the CD t = cdrdao.ReadTOCTask(device=device) try: runner.run(t) except cdrdao.DeviceOpenException, e: self.error(e.msg) return 3
def do(self, args): prog = program.Program(self.getRootCommand.config(), stdout=self.stdout) runner = task.SyncRunner() for arg in args: self.stdout.write('Renaming image %r\n' % arg) arg = arg.decode('utf-8') cueImage = image.Image(arg) cueImage.setup(runner) mbdiscid = cueImage.table.getMusicBrainzDiscId() operator = renamer.Operator(statePath, mbdiscid) self.stdout.write('MusicBrainz disc id is %s\n' % mbdiscid) prog.metadata = prog.getMusicBrainz( cueImage.table, mbdiscid, release=self.options.release_id) if not prog.metadata: print 'Not in MusicBrainz database, skipping' continue # FIXME: this feels like we're poking at internals. prog.cuePath = arg prog.result = result.RipResult() for track in cueImage.table.tracks: path = cueImage.getRealPath(track.indexes[1].path) taglist = prog.getTagList(track.number) self.debug( 'possibly retagging %r from cue path %r with taglist %r', path, arg, taglist) t = encode.SafeRetagTask(path, taglist) runner.run(t) path = os.path.basename(path) if t.changed: print 'Retagged %s' % path else: print '%s already tagged correctly' % path print
def do(self, args): prog = program.Program(self.getRootCommand().config) runner = task.SyncRunner() cache = accurip.AccuCache() for arg in args: arg = arg.decode('utf-8') cueImage = image.Image(arg) cueImage.setup(runner) url = cueImage.table.getAccurateRipURL() responses = cache.retrieve(url) # FIXME: this feels like we're poking at internals. prog.cuePath = arg prog.result = result.RipResult() for track in cueImage.table.tracks: tr = result.TrackResult() tr.number = track.number prog.result.tracks.append(tr) prog.verifyImage(runner, responses) print "\n".join(prog.getAccurateRipResults()) + "\n"
def testLength(self): path = os.path.join(os.path.dirname(__file__), u'track.flac') t = image.AudioLengthTask(path) runner = task.SyncRunner() runner.run(t, verbose=False) self.assertEquals(t.length, 10 * common.SAMPLES_PER_FRAME)
def setUp(self): self.image = image.Image( os.path.join(os.path.dirname(__file__), u'track-separate.cue')) self.runner = task.SyncRunner(verbose=False) self.image.setup(self.runner)
def do(self, args): prog = program.Program(record=self.getRootCommand().record) runner = task.SyncRunner() def function(r, t): r.run(t) # if the device is mounted (data session), unmount it device = self.parentCommand.options.device self.stdout.write('Checking device %s\n' % device) prog.loadDevice(device) prog.unmountDevice(device) version = None # first, read the normal TOC, which is fast ptoc = common.Persister(self.options.toc_pickle or None) if not ptoc.object: t = cdrdao.ReadTOCTask(device=device) function(runner, t) version = t.tasks[1].parser.version from pkg_resources import parse_version as V # we've built a cdrdao 1.2.3rc2 modified package with the patch if V(version) < V('1.2.3rc2p1'): self.stdout.write(''' Warning: cdrdao older than 1.2.3 has a pre-gap length bug. See http://sourceforge.net/tracker/?func=detail&aid=604751&group_id=2171&atid=102171 ''') ptoc.persist(t.table) ittoc = ptoc.object assert ittoc.hasTOC() # already show us some info based on this prog.getRipResult(ittoc.getCDDBDiscId()) self.stdout.write("CDDB disc id: %s\n" % ittoc.getCDDBDiscId()) mbdiscid = ittoc.getMusicBrainzDiscId() self.stdout.write("MusicBrainz disc id %s\n" % mbdiscid) self.stdout.write("MusicBrainz lookup URL %s\n" % ittoc.getMusicBrainzSubmitURL()) prog.metadata = prog.getMusicBrainz(ittoc, mbdiscid, self.options.release) if not prog.metadata: # fall back to FreeDB for lookup cddbid = ittoc.getCDDBValues() cddbmd = prog.getCDDB(cddbid) if cddbmd: self.stdout.write('FreeDB identifies disc as %s\n' % cddbmd) if not self.options.unknown: prog.ejectDevice(device) return -1 # now, read the complete index table, which is slower itable = prog.getTable(runner, ittoc.getCDDBDiscId(), device) assert itable.getCDDBDiscId() == ittoc.getCDDBDiscId(), \ "full table's id %s differs from toc id %s" % ( itable.getCDDBDiscId(), ittoc.getCDDBDiscId()) assert itable.getMusicBrainzDiscId() == ittoc.getMusicBrainzDiscId(), \ "full table's mb id %s differs from toc id mb %s" % ( itable.getMusicBrainzDiscId(), ittoc.getMusicBrainzDiscId()) assert itable.getAccurateRipURL() == ittoc.getAccurateRipURL(), \ "full table's AR URL %s differs from toc AR URL %s" % ( itable.getAccurateRipURL(), ittoc.getAccurateRipURL()) prog.outdir = (self.options.output_directory or os.getcwd()) prog.outdir = prog.outdir.decode('utf-8') # here to avoid import gst eating our options from morituri.common import encode profile = encode.PROFILES[self.options.profile]() # result prog.result.cdrdao_version = version prog.result.cdparanoia_version = cdparanoia.ParanoiaVersion() prog.result.offset = int(self.options.offset) prog.result.artist = prog.metadata and prog.metadata.artist \ or 'Unknown Artist' prog.result.title = prog.metadata and prog.metadata.title \ or 'Unknown Title' # cdio is optional for now try: import cdio _, prog.result.vendor, prog.result.model, prog.result.release = \ cdio.Device(device).get_hwinfo() except ImportError: self.stdout.write( 'WARNING: pycdio not installed, cannot identify drive\n') prog.result.vendor = 'Unknown' prog.result.model = 'Unknown' prog.result.release = 'Unknown' # FIXME: turn this into a method def ripIfNotRipped(number): # we can have a previous result trackResult = prog.result.getTrackResult(number) if not trackResult: trackResult = result.TrackResult() prog.result.tracks.append(trackResult) path = prog.getPath(prog.outdir, self.options.track_template, mbdiscid, number) + '.' + profile.extension trackResult.number = number assert type(path) is unicode, "%r is not unicode" % path trackResult.filename = path if number > 0: trackResult.pregap = itable.tracks[number - 1].getPregap() # FIXME: optionally allow overriding reripping if os.path.exists(path): self.stdout.write('Verifying track %d of %d: %s\n' % (number, len(itable.tracks), os.path.basename(path).encode('utf-8'))) if not prog.verifyTrack(runner, trackResult): self.stdout.write('Verification failed, reripping...\n') os.unlink(path) if not os.path.exists(path): tries = 0 self.stdout.write('Ripping track %d of %d: %s\n' % (number, len(itable.tracks), os.path.basename(path).encode('utf-8'))) while tries < MAX_TRIES: tries += 1 try: self.debug('ripIfNotRipped: track %d, try %d', number, tries) prog.ripTrack(runner, trackResult, offset=int(self.options.offset), device=self.parentCommand.options.device, profile=profile, taglist=prog.getTagList(number), what='track %d of %d' % (number, len(itable.tracks))) break except Exception, e: self.debug('Got exception %r on try %d', e, tries) if tries == MAX_TRIES: self.error('Giving up on track %d after %d times' % (number, tries)) if trackResult.testcrc == trackResult.copycrc: self.stdout.write('Checksums match for track %d\n' % number) else: self.stdout.write( 'ERROR: checksums did not match for track %d\n' % number) raise self.stdout.write('Peak level: %.2f %%\n' % (math.sqrt(trackResult.peak) * 100.0, )) self.stdout.write('Rip quality: %.2f %%\n' % (trackResult.quality * 100.0, )) # overlay this rip onto the Table if number == 0: # HTOA goes on index 0 of track 1 itable.setFile(1, 0, trackResult.filename, ittoc.getTrackStart(1), number) else: itable.setFile(number, 1, trackResult.filename, ittoc.getTrackLength(number), number) prog.saveRipResult()