def logRip(self, ripResult, epoch): lines = [] ### global lines.append("Logfile created by: morituri %s" % configure.version) # FIXME: when we localize this, see #49 to handle unicode properly. import locale old = locale.getlocale(locale.LC_TIME) locale.setlocale(locale.LC_TIME, 'C') date = time.strftime("%b %d %H:%M:%S", time.localtime(epoch)) locale.setlocale(locale.LC_TIME, old) lines.append("Logfile created on: %s" % date) lines.append("") # album lines.append("Album: %s - %s" % (ripResult.artist, ripResult.title)) lines.append("") lines.append("CDDB disc id: %s" % ripResult.table.getCDDBDiscId()) lines.append("MusicBrainz disc id: %s" % ripResult.table.getMusicBrainzDiscId()) lines.append("MusicBrainz lookup URL: %s" % ripResult.table.getMusicBrainzSubmitURL()) lines.append("") # drive lines.append("Drive: vendor %s, model %s" % (ripResult.vendor, ripResult.model)) lines.append("") lines.append("Read offset correction: %d" % ripResult.offset) lines.append("") # toc lines.append("Table of Contents:") lines.append("") lines.append(" Track | Start | Length") lines.append(" ------------------------------------------------") table = ripResult.table for t in table.tracks: start = t.getIndex(1).absolute length = table.getTrackLength(t.number) lines.append(" %2d | %6d - %s | %6d - %s" % (t.number, start, common.framesToMSF(start), length, common.framesToMSF(length))) lines.append("") lines.append("") ### per-track for t in ripResult.tracks: lines.extend(self.trackLog(t)) lines.append('') return lines
def trackLog(self, trackResult): """Returns Tracks section lines: data picked from trackResult""" lines = [] # Track number lines.append(" %02d:" % trackResult.number) # Filename (including path) of ripped track lines.append(" Filename: %s" % trackResult.filename) # Pre-gap length pregap = trackResult.pregap if pregap: lines.append(" Pre-gap length: %s" % common.framesToMSF(pregap)) # Peak level peak = trackResult.peak lines.append(" Peak level: %.6f" % peak) # Extraction speed if trackResult.copyspeed: lines.append(" Extraction speed: %.1f X" % (trackResult.copyspeed)) # Extraction quality if trackResult.quality and trackResult.quality > 0.001: lines.append(" Extraction quality: %.2f %%" % (trackResult.quality * 100.0,)) # Ripper Test CRC if trackResult.testcrc is not None: lines.append(" Test CRC: %08X" % trackResult.testcrc) # Ripper Copy CRC if trackResult.copycrc is not None: lines.append(" Copy CRC: %08X" % trackResult.copycrc) # AccurateRip track status # Currently there's no support for AccurateRip V2 if trackResult.accurip: lines.append(" AccurateRip V1:") self._inARDatabase += 1 if trackResult.ARCRC == trackResult.ARDBCRC: lines.append(" Result: Found, exact match") self._accuratelyRipped += 1 else: lines.append(" Result: Found, NO exact match") lines.append(" Confidence: %d" % trackResult.ARDBConfidence) lines.append(" Local CRC: %08X" % trackResult.ARCRC) lines.append(" Remote CRC: %08X" % trackResult.ARDBCRC) elif trackResult.number != 0: lines.append(" AccurateRip V1:") lines.append(" Result: Track not present in " "AccurateRip database") # Check if Test & Copy CRCs are equal if trackResult.testcrc == trackResult.copycrc: lines.append(" Status: Copy OK") else: self._errors = True lines.append(" Status: Error, CRC mismatch") return lines
def trackLog(self, trackResult): lines = [] lines.append('Track %2d' % trackResult.number) lines.append('') lines.append(' Filename %s' % trackResult.filename) lines.append('') if trackResult.pregap: lines.append(' Pre-gap length %s' % common.framesToMSF( trackResult.pregap)) lines.append('') lines.append(' Peak level %.06f' % trackResult.peak) if trackResult.testspeed: lines.append(' Extraction Speed (Test) %.4f X' % ( trackResult.testspeed)) if trackResult.copyspeed: lines.append(' Extraction Speed (Copy) %.4f X' % ( trackResult.copyspeed)) if trackResult.testcrc: lines.append(' Test CRC %08X' % trackResult.testcrc) if trackResult.copycrc: lines.append(' Copy CRC %08X' % trackResult.copycrc) if trackResult.ARCRC: lines.append(' AccurateRip signature %08X' % trackResult.ARCRC) if trackResult.accurip: lines.append(' Accurately ripped (confidence %d)' % ( trackResult.ARDBConfidence)) else: if trackResult.ARDBCRC: lines.append(' Cannot be verified as accurate, ' 'AccurateRip returned [%08X]' % ( trackResult.ARDBCRC)) else: lines.append(' Track not present in AccurateRip database') if trackResult.testcrc: if trackResult.testcrc == trackResult.copycrc: lines.append(' Copy OK') else: lines.append(" WARNING: CRCs don't match!") else: lines.append(" WARNING: no CRC check done") return lines
def trackLog(self, trackResult): lines = [] lines.append(" %02d:" % trackResult.number) lines.append(" Filename: %s" % trackResult.filename) pregap = trackResult.pregap if pregap: lines.append(" Pre-gap length: %s" % common.framesToMSF(pregap)) peak = trackResult.peak lines.append(" Peak level: %.6f %%" % peak) if trackResult.copyspeed: lines.append(" Extraction speed: %.1f X" % ( trackResult.copyspeed)) if trackResult.quality and trackResult.quality > 0.001: lines.append(" Track quality: %.2f %%" % (trackResult.quality * 100.0, )) if trackResult.testcrc is not None: lines.append(" Test CRC: %08X" % trackResult.testcrc) if trackResult.copycrc is not None: lines.append(" Copy CRC: %08X" % trackResult.copycrc) if trackResult.accurip: lines.append(" AccurateRip v1:") self._inARDatabase += 1 if trackResult.ARCRC == trackResult.ARDBCRC: lines.append(" Result: Found, exact match") lines.append(" Confidence: %d" % trackResult.ARDBConfidence) lines.append(" Checksum: %08X" % trackResult.ARCRC) self._accuratelyRipped += 1 else: lines.append(" Result: Found, NO exact match " "(confidence %d [%08X], AccurateRip " "returned [%08x])" % (trackResult.ARDBConfidence, trackResult.ARCRC, trackResult.ARDBCRC)) elif trackResult.number != 0: lines.append(" AccurateRip v1:") lines.append(" Result: Track not present in " "AccurateRip database") if trackResult.testcrc == trackResult.copycrc: lines.append(" Status: Copy OK") else: self._errors = True lines.append(" Status: Error, CRC mismatch") return lines
def trackLog(self, trackResult): lines = [] lines.append('Track %2d' % trackResult.number) lines.append('') lines.append(' Filename %s' % trackResult.filename) lines.append('') if trackResult.pregap: lines.append(' Pre-gap: %s' % common.framesToMSF( trackResult.pregap)) lines.append('') lines.append(' Peak level %.1f %%' % (trackResult.peak * 100.0)) if trackResult.copyspeed: lines.append(' Extraction Speed (Copy) %.4f X' % ( trackResult.copyspeed)) if trackResult.testspeed: lines.append(' Extraction Speed (Test) %.4f X' % ( trackResult.testspeed)) if trackResult.copycrc is not None: lines.append(' Copy CRC %08X' % trackResult.copycrc) if trackResult.testcrc is not None: lines.append(' Test CRC %08X' % trackResult.testcrc) if trackResult.testcrc == trackResult.copycrc: lines.append(' Copy OK') else: lines.append(" WARNING: CRCs don't match!") else: lines.append(" WARNING: no CRC check done") if trackResult.accurip: lines.append(' Accurately ripped (confidence %d) [%08X]' % ( trackResult.ARDBConfidence, trackResult.ARCRC)) else: if trackResult.ARDBCRC: lines.append(' Cannot be verified as accurate ' '[%08X], AccurateRip returned [%08X]' % ( trackResult.ARCRC, trackResult.ARDBCRC)) else: lines.append(' Track not present in AccurateRip database') return lines
def trackLog(self, trackResult): lines = [] lines.append("Track %2d" % trackResult.number) lines.append("") lines.append(" Filename %s" % trackResult.filename) lines.append("") if trackResult.pregap: lines.append(" Pre-gap length %s" % common.framesToMSF(trackResult.pregap)) lines.append("") lines.append(" Peak level %.06f" % trackResult.peak) if trackResult.testspeed: lines.append(" Extraction Speed (Test) %.4f X" % (trackResult.testspeed)) if trackResult.copyspeed: lines.append(" Extraction Speed (Copy) %.4f X" % (trackResult.copyspeed)) if trackResult.testcrc: lines.append(" Test CRC %08X" % trackResult.testcrc) if trackResult.copycrc: lines.append(" Copy CRC %08X" % trackResult.copycrc) if trackResult.ARCRC: lines.append(" AccurateRip signature %08X" % trackResult.ARCRC) if trackResult.accurip: lines.append(" Accurately ripped (confidence %d)" % (trackResult.ARDBConfidence)) else: if trackResult.ARDBCRC: lines.append( " Cannot be verified as accurate, " "AccurateRip returned [%08X]" % (trackResult.ARDBCRC) ) else: lines.append(" Track not present in AccurateRip database") if trackResult.testcrc: if trackResult.testcrc == trackResult.copycrc: lines.append(" Copy OK") else: lines.append(" WARNING: CRCs don't match!") else: lines.append(" WARNING: no CRC check done") return lines
def trackLog(self, trackResult): lines = [] lines.append(" %02d:" % trackResult.number) lines.append(" Filename: %s" % trackResult.filename) pregap = trackResult.pregap if pregap: lines.append(" Pre-gap length: %s" % common.framesToMSF(pregap)) peak = trackResult.peak lines.append(" Peak level: %.6f %%" % peak) if trackResult.copyspeed: lines.append(" Extraction speed: %.1f X" % (trackResult.copyspeed)) if trackResult.quality and trackResult.quality > 0.001: lines.append(" Track quality: %.2f %%" % (trackResult.quality * 100.0, )) if trackResult.testcrc is not None: lines.append(" Test CRC: %08X" % trackResult.testcrc) if trackResult.copycrc is not None: lines.append(" Copy CRC: %08X" % trackResult.copycrc) if trackResult.accurip: lines.append(" AccurateRip V1:") self._inARDatabase += 1 if trackResult.ARCRC == trackResult.ARDBCRC: lines.append(" Result: Found, exact match") self._accuratelyRipped += 1 else: lines.append(" Result: Found, NO exact match") lines.append(" Confidence: %d" % trackResult.ARDBConfidence) lines.append(" Local CRC: %08X" % trackResult.ARCRC) lines.append(" Remote CRC: %08X" % trackResult.ARDBCRC) elif trackResult.number != 0: lines.append(" AccurateRip V1:") lines.append(" Result: Track not present in " "AccurateRip database") if trackResult.testcrc == trackResult.copycrc: lines.append(" Status: Copy OK") else: self._errors = True lines.append(" Status: Error, CRC mismatch") return lines
def cue(self, cuePath='', program='morituri'): """ @param cuePath: path to the cue file to be written. If empty, will treat paths as if in current directory. Dump our internal representation to a .cue file content. @rtype: C{unicode} """ self.debug('generating .cue for cuePath %r', cuePath) lines = [] def writeFile(path): targetPath = common.getRelativePath(path, cuePath) line = 'FILE "%s" WAVE' % targetPath lines.append(line) self.debug('writeFile: %r' % line) # header main = ['PERFORMER', 'TITLE'] for key in CDTEXT_FIELDS: if key not in main and key in self.cdtext: lines.append(" %s %s" % (key, self.cdtext[key])) assert self.hasTOC(), "Table does not represent a full CD TOC" lines.append('REM DISCID %s' % self.getCDDBDiscId().upper()) lines.append('REM COMMENT "%s %s"' % (program, configure.version)) if self.catalog: lines.append("CATALOG %s" % self.catalog) for key in main: if key in self.cdtext: lines.append('%s "%s"' % (key, self.cdtext[key])) # FIXME: # - the first FILE statement goes before the first TRACK, even if # there is a non-file-using PREGAP # - the following FILE statements come after the last INDEX that # use that FILE; so before a next TRACK, PREGAP silence, ... # add the first FILE line; EAC always puts the first FILE # statement before TRACK 01 and any possible PRE-GAP firstTrack = self.tracks[0] index = firstTrack.getFirstIndex() indexOne = firstTrack.getIndex(1) counter = index.counter track = firstTrack while not index.path: t, i = self.getNextTrackIndex(track.number, index.number) track = self.tracks[t - 1] index = track.getIndex(i) counter = index.counter if index.path: self.debug('counter %d, writeFile' % counter) writeFile(index.path) for i, track in enumerate(self.tracks): self.debug('track i %r, track %r' % (i, track)) # FIXME: skip data tracks for now if not track.audio: continue indexes = track.indexes.keys() indexes.sort() wroteTrack = False for number in indexes: index = track.indexes[number] self.debug('index %r, %r' % (number, index)) # any time the source counter changes to a higher value, # write a FILE statement # it has to be higher, because we can run into the HTOA # at counter 0 here if index.counter > counter: if index.path: self.debug('counter %d, writeFile' % counter) writeFile(index.path) self.debug('setting counter to index.counter %r' % index.counter) counter = index.counter # any time we hit the first index, write a TRACK statement if not wroteTrack: wroteTrack = True line = " TRACK %02d %s" % (i + 1, 'AUDIO') lines.append(line) self.debug('%r' % line) for key in CDTEXT_FIELDS: if key in track.cdtext: lines.append(' %s "%s"' % ( key, track.cdtext[key])) if track.isrc is not None: lines.append(" ISRC %s" % track.isrc) # handle TRACK 01 INDEX 00 specially if 0 in indexes: index00 = track.indexes[0] if i == 0: # if we have a silent pre-gap, output it if not index00.path: length = indexOne.absolute - index00.absolute lines.append(" PREGAP %s" % common.framesToMSF(length)) continue # handle any other INDEX 00 after its TRACK lines.append(" INDEX %02d %s" % (0, common.framesToMSF(index00.relative))) if number > 0: # index 00 is output after TRACK up above lines.append(" INDEX %02d %s" % (number, common.framesToMSF(index.relative))) lines.append("") return "\n".join(lines)
def logRip(self, ripResult, epoch): """Returns logfile lines list""" lines = [] # Ripper version # Only implemented in whipper (ripResult.logger) lines.append("Log created by: whipper %s (%s logger)" % ( morituri.__version__, ripResult.logger)) # Rip date date = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(epoch)).strip() lines.append("Log creation date: %s" % date) lines.append("") # Rip technical settings lines.append("Ripping phase information:") lines.append(" Drive: %s%s (revision %s)" % ( ripResult.vendor, ripResult.model, ripResult.release)) if ripResult.cdparanoiaDefeatsCache is None: defeat = "Unknown" elif ripResult.cdparanoiaDefeatsCache: defeat = "Yes" else: defeat = "No" lines.append(" Defeat audio cache: %s" % defeat) lines.append(" Read offset correction: %+d" % ripResult.offset) # Currently unsupported by the official cdparanoia package over = "No" # Only implemented in whipper (ripResult.overread) if ripResult.overread: over = "Yes" lines.append(" Overread into lead-out: %s" % over) # Next one fully works only using the patched cdparanoia package # lines.append("Fill up missing offset samples with silence: Yes") lines.append(" Gap detection: cdrdao %s" % ripResult.cdrdaoVersion) lines.append("") # Rip encoding settings lines.append("Encoding phase information:") lines.append(" Used output format: %s" % ripResult.profileName) lines.append(" GStreamer:") lines.append(" Pipeline: %s" % ripResult.profilePipeline) lines.append(" Version: %s" % ripResult.gstreamerVersion) lines.append(" Python version: %s" % ripResult.gstPythonVersion) lines.append(" Encoder plugin version: %s" % ripResult.encoderVersion) lines.append("") # CD metadata lines.append("CD metadata:") lines.append(" Album: %s - %s" % (ripResult.artist, ripResult.title)) lines.append(" CDDB Disc ID: %s" % ripResult. table.getCDDBDiscId()) lines.append(" MusicBrainz Disc ID: %s" % ripResult. table.getMusicBrainzDiscId()) lines.append(" MusicBrainz lookup url: %s" % ripResult. table.getMusicBrainzSubmitURL()) lines.append("") # TOC section lines.append("TOC:") table = ripResult.table # Test for HTOA presence htoa = None try: htoa = table.tracks[0].getIndex(0) except KeyError: pass # If True, include HTOA line into log's TOC if htoa and htoa.path: htoastart = htoa.absolute htoaend = table.getTrackEnd(0) htoalength = table.tracks[0].getIndex(1).absolute - htoastart lines.append(" 00:") lines.append(" Start: %s" % common.framesToMSF(htoastart)) lines.append(" Length: %s" % common.framesToMSF(htoalength)) lines.append(" Start sector: %d" % htoastart) lines.append(" End sector: %d" % htoaend) lines.append("") # For every track include information in the TOC for t in table.tracks: # FIXME: what happens to a track start over 60 minutes ? # Answer: tested empirically, everything seems OK start = t.getIndex(1).absolute length = table.getTrackLength(t.number) end = table.getTrackEnd(t.number) lines.append(" %02d:" % t.number) lines.append(" Start: %s" % common.framesToMSF(start)) lines.append(" Length: %s" % common.framesToMSF(length)) lines.append(" Start sector: %d" % start) lines.append(" End sector: %d" % end) lines.append("") # Tracks section lines.append("Tracks:") duration = 0.0 for t in ripResult.tracks: if not t.filename: continue lines.extend(self.trackLog(t)) lines.append("") duration += t.testduration + t.copyduration # Status report lines.append("Conclusive status report:") arHeading = " AccurateRip summary:" if self._inARDatabase == 0: lines.append("%s None of the tracks are present in the " "AccurateRip database" % arHeading) else: nonHTOA = len(ripResult.tracks) if ripResult.tracks[0].number == 0: nonHTOA -= 1 if self._accuratelyRipped == 0: lines.append("%s No tracks could be verified as accurate " "(you may have a different pressing from the " "one(s) in the database)" % arHeading) elif self._accuratelyRipped < nonHTOA: accurateTracks = nonHTOA - self._accuratelyRipped lines.append("%s Some tracks could not be verified as " "accurate (%d/%d got no match)" % ( arHeading, accurateTracks, nonHTOA)) else: lines.append("%s All tracks accurately ripped" % arHeading) hsHeading = " Health status:" if self._errors: lines.append("%s There were errors" % hsHeading) else: lines.append("%s No errors occurred" % hsHeading) lines.append(" EOF: End of status report") lines.append("") # Log hash hasher = hashlib.sha256() hasher.update("\n".join(lines).encode("utf-8")) lines.append("SHA-256 hash: %s" % hasher.hexdigest().upper()) lines.append("") return lines
def logRip(self, ripResult, epoch): lines = [] ### global lines.append("Logfile created by: morituri %s" % configure.version) # FIXME: when we localize this, see #49 to handle unicode properly. import locale old = locale.getlocale(locale.LC_TIME) locale.setlocale(locale.LC_TIME, 'C') date = time.strftime("%b %d %H:%M:%S", time.localtime(epoch)) locale.setlocale(locale.LC_TIME, old) lines.append("Logfile created on: %s" % date) lines.append("") # album lines.append("Album: %s - %s" % (ripResult.artist, ripResult.title)) lines.append("") lines.append("CDDB disc id: %s" % ripResult. table.getCDDBDiscId()) lines.append("MusicBrainz disc id: %s" % ripResult. table.getMusicBrainzDiscId()) lines.append("MusicBrainz lookup URL: %s" % ripResult. table.getMusicBrainzSubmitURL()) lines.append("") # drive lines.append( "Drive: vendor %s, model %s" % ( ripResult.vendor, ripResult.model)) lines.append("") lines.append("Read offset correction: %d" % ripResult.offset) lines.append("") # toc lines.append("Table of Contents:") lines.append("") lines.append( " Track | Start | Length") lines.append( " ------------------------------------------------") table = ripResult.table for t in table.tracks: start = t.getIndex(1).absolute length = table.getTrackLength(t.number) lines.append( " %2d | %6d - %s | %6d - %s" % ( t.number, start, common.framesToMSF(start), length, common.framesToMSF(length))) lines.append("") lines.append("") ### per-track for t in ripResult.tracks: lines.extend(self.trackLog(t)) lines.append('') return lines
def cue(self, cuePath='', program='morituri'): """ @param cuePath: path to the cue file to be written. If empty, will treat paths as if in current directory. Dump our internal representation to a .cue file content. @rtype: C{unicode} """ self.debug('generating .cue for cuePath %r', cuePath) lines = [] def writeFile(path): targetPath = common.getRelativePath(path, cuePath) line = 'FILE "%s" WAVE' % targetPath lines.append(line) self.debug('writeFile: %r' % line) # header main = ['PERFORMER', 'TITLE'] for key in CDTEXT_FIELDS: if key not in main and key in self.cdtext: lines.append(" %s %s" % (key, self.cdtext[key])) assert self.hasTOC(), "Table does not represent a full CD TOC" lines.append('REM DISCID %s' % self.getCDDBDiscId().upper()) lines.append('REM COMMENT "%s %s"' % (program, configure.version)) if self.catalog: lines.append("CATALOG %s" % self.catalog) for key in main: if key in self.cdtext: lines.append('%s "%s"' % (key, self.cdtext[key])) # FIXME: # - the first FILE statement goes before the first TRACK, even if # there is a non-file-using PREGAP # - the following FILE statements come after the last INDEX that # use that FILE; so before a next TRACK, PREGAP silence, ... # add the first FILE line; EAC always puts the first FILE # statement before TRACK 01 and any possible PRE-GAP firstTrack = self.tracks[0] index = firstTrack.getFirstIndex() indexOne = firstTrack.getIndex(1) counter = index.counter track = firstTrack while not index.path: t, i = self.getNextTrackIndex(track.number, index.number) track = self.tracks[t - 1] index = track.getIndex(i) counter = index.counter if index.path: self.debug('counter %d, writeFile' % counter) writeFile(index.path) for i, track in enumerate(self.tracks): self.debug('track i %r, track %r' % (i, track)) # FIXME: skip data tracks for now if not track.audio: continue indexes = track.indexes.keys() indexes.sort() wroteTrack = False for number in indexes: index = track.indexes[number] self.debug('index %r, %r' % (number, index)) # any time the source counter changes to a higher value, # write a FILE statement # it has to be higher, because we can run into the HTOA # at counter 0 here if index.counter > counter: if index.path: self.debug('counter %d, writeFile' % counter) writeFile(index.path) self.debug('setting counter to index.counter %r' % index.counter) counter = index.counter # any time we hit the first index, write a TRACK statement if not wroteTrack: wroteTrack = True line = " TRACK %02d %s" % (i + 1, 'AUDIO') lines.append(line) self.debug('%r' % line) for key in CDTEXT_FIELDS: if key in track.cdtext: lines.append(' %s "%s"' % (key, track.cdtext[key])) if track.isrc is not None: lines.append(" ISRC %s" % track.isrc) if track.pre_emphasis is not None: lines.append(" FLAGS PRE") # handle TRACK 01 INDEX 00 specially if 0 in indexes: index00 = track.indexes[0] if i == 0: # if we have a silent pre-gap, output it if not index00.path: length = indexOne.absolute - index00.absolute lines.append(" PREGAP %s" % common.framesToMSF(length)) continue # handle any other INDEX 00 after its TRACK lines.append(" INDEX %02d %s" % (0, common.framesToMSF(index00.relative))) if number > 0: # index 00 is output after TRACK up above lines.append(" INDEX %02d %s" % (number, common.framesToMSF(index.relative))) lines.append("") return "\n".join(lines)
def logRip(self, ripResult, epoch): lines = [] ### global lines.append("morituri version %s" % configure.version) lines.append("") # FIXME: when we localize this, see #49 to handle unicode properly. import locale old = locale.getlocale(locale.LC_TIME) locale.setlocale(locale.LC_TIME, 'C') date = time.strftime("%b %d %H:%M:%S", time.localtime(epoch)) locale.setlocale(locale.LC_TIME, old) lines.append("morituri logfile from %s" % date) lines.append("") # album lines.append("%s / %s" % (ripResult.artist, ripResult.title)) lines.append("") # drive lines.append( "Used Drive : %s %s %s" % ( ripResult.vendor, ripResult.model, ripResult.release)) lines.append("") # Default for cdparanoia lines.append("Use cdparanoia mode : Yes (%s)" % ( ripResult.cdparanoia_version)) # Default for cdparanoia by virtue of ripping whole tracks at a time lines.append("Defeat audio cache : Yes") # Default for cdparanoia by virtue of having no C2 rip mode lines.append("Make use of C2 pointers : No") lines.append("") lines.append("Read offset correction : %d" % ( ripResult.offset)) # Currently unsupported by cdparanoia lines.append("Overread into Lead-In and Lead-Out : No") # Default for cdparanoia lines.append("Fill up missing offset samples with silence : Yes") # Default for cdparanoia lines.append("Delete leading and trailing silent blocks : No") # Default lines.append("Null samples used in CRC calculations : Yes") lines.append("Gap Detection : " "cdrdao version %s" % ripResult.cdrdao_version) # Default for cdparanoia lines.append("Gap handling : " "Appended to previous track") lines.append("") # toc lines.append("TOC of the extracted CD") lines.append("") lines.append( " Track | Start | Length | Start sector | End sector") lines.append( " ---------------------------------------------------------") table = ripResult.table for t in table.tracks: start = t.getIndex(1).absolute length = table.getTrackLength(t.number) lines.append( " %2d | %s | %s | %9d | %8d" % ( t.number, common.framesToMSF(start), common.framesToMSF(length), start, start + length - 1)) lines.append("") lines.append("") ### per-track for t in ripResult.tracks: lines.extend(self.trackLog(t)) lines.append('') return lines
def cue(self, cuePath='', program='Morituri'): """ @param cuePath: path to the cue file to be written. If empty, will treat paths as if in current directory. Dump our internal representation to a .cue file content. @rtype: C{unicode} """ lines = [] def writeFile(path): targetPath = common.getRelativePath(path, cuePath) lines.append('FILE "%s" WAVE' % targetPath) # header main = ['PERFORMER', 'TITLE'] for key in CDTEXT_FIELDS: if key not in main and key in self.cdtext: lines.append(" %s %s" % (key, self.cdtext[key])) assert self.hasTOC(), "Table does not represent a full CD TOC" lines.append('REM DISCID %s' % self.getCDDBDiscId().upper()) lines.append('REM COMMENT "%s"' % program) if self.catalog: lines.append("CATALOG %s" % self.catalog) for key in main: if key in self.cdtext: lines.append('%s "%s"' % (key, self.cdtext[key])) # add the first FILE line path = self.tracks[0].getFirstIndex().path counter = self.tracks[0].getFirstIndex().counter writeFile(path) for i, track in enumerate(self.tracks): # FIXME: skip data tracks for now if not track.audio: continue # if there is no index 0, but there is a new file, advance # FILE line here if not 0 in track.indexes: index = track.indexes[1] if index.counter != counter: writeFile(index.path) counter = index.counter lines.append(" TRACK %02d %s" % (i + 1, 'AUDIO')) for key in CDTEXT_FIELDS: if key in track.cdtext: lines.append(' %s "%s"' % (key, track.cdtext[key])) if track.isrc is not None: lines.append(" ISRC %s" % track.isrc) indexes = track.indexes.keys() indexes.sort() for number in indexes: index = track.indexes[number] if index.counter != counter: writeFile(index.path) counter = index.counter lines.append(" INDEX %02d %s" % (number, common.framesToMSF(index.relative))) lines.append("") return "\n".join(lines)
def logRip(self, ripResult, epoch): lines = [] lines.append("Ripper: morituri %s" % configure.version) date = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(epoch)).strip() lines.append("Ripped at: %s" % date) lines.append("Drive: %s%s (revision %s)" % (ripResult.vendor, ripResult.model, ripResult.release)) lines.append("") defeat = "Unknown" if ripResult.cdparanoiaDefeatsCache is True: defeat = "Yes" elif ripResult.cdparanoiaDefeatsCache is False: defeat = "No" lines.append("Defeat audio cache: %s" % defeat) lines.append("Read offset correction: %+d" % ripResult.offset) # Currently unsupported by the official cdparanoia package over = "Unknown" if ripResult.overread is True: over = "Yes" elif ripResult.overread is False: over = "No" lines.append("Overread into lead-out: %s" % over) # Next one fully works only using the patched cdparanoia package # lines.append("Fill up missing offset samples with silence: Yes") lines.append("Gap detection: cdrdao %s" % ripResult.cdrdaoVersion) lines.append("") lines.append("Used output format: %s" % ripResult.profileName) lines.append("GStreamer:") lines.append(" Pipeline: %s" % ripResult.profilePipeline) lines.append(" Version: %s" % ripResult.gstreamerVersion) lines.append(" Python version: %s" % ripResult.gstPythonVersion) lines.append(" Encoder plugin version: %s" % ripResult.encoderVersion) lines.append("") lines.append("TOC:") table = ripResult.table htoa = None try: htoa = table.tracks[0].getIndex(0) except KeyError: pass if htoa and htoa.path: htoastart = htoa.absolute htoaend = table.getTrackEnd(0) htoalength = table.tracks[0].getIndex(1).absolute - htoastart lines.append(" 00:") lines.append(" Start: %s" % common.framesToMSF(htoastart)) lines.append(" Length: %s" % common.framesToMSF(htoalength)) lines.append(" Start sector: %d" % htoastart) lines.append(" End sector: %d" % htoaend) for t in table.tracks: # FIXME: what happens to a track start over 60 minutes ? # Answer: tested empirically, everything seems OK start = t.getIndex(1).absolute length = table.getTrackLength(t.number) end = table.getTrackEnd(t.number) lines.append(" %02d:" % t.number) lines.append(" Start: %s" % common.framesToMSF(start)) lines.append(" Length: %s" % common.framesToMSF(length)) lines.append(" Start sector: %d" % start) lines.append(" End sector: %d" % end) lines.append("") lines.append("Tracks:") duration = 0.0 for t in ripResult.tracks: if not t.filename: continue lines.extend(self.trackLog(t)) lines.append("") duration += t.testduration + t.copyduration lines.append("Information:") lines.append(" AccurateRip summary:") if self._inARDatabase == 0: lines.append(" Result: None of the tracks are present in " "the AccurateRip database") else: nonHTOA = len(ripResult.tracks) if ripResult.tracks[0].number == 0: nonHTOA -= 1 if self._accuratelyRipped == 0: lines.append(" Result: No tracks could be verified as " "accurate (you may have a different pressing " "from the one(s) in the database") elif self._accuratelyRipped < nonHTOA: lines.append(" %d track(s) accurately ripped" % self._accuratelyRipped) lines.append(" %d track(s) could not be verified as " "accurate" % (nonHTOA - self._accuratelyRipped)) lines.append("") lines.append(" Result: Some tracks could not be verified " "as accurate (%d/%d got no match)" % ( (nonHTOA - self._accuratelyRipped), nonHTOA)) else: lines.append(" Result: All tracks accurately ripped") lines.append("") lines.append(" Health status:") if self._errors: lines.append(" Result: There were errors") else: lines.append(" Result: No errors occurred") lines.append("") lines.append(" EOF: End of status report") lines.append("") hasher = hashlib.sha256() hasher.update("\n".join(lines).encode("utf-8")) lines.append("SHA-256 hash: %s" % hasher.hexdigest().upper()) lines.append("") return lines
def logRip(self, ripResult, epoch): """Returns logfile lines list""" lines = [] # Ripper version # Only implemented in whipper (ripResult.logger) lines.append("Log created by: whipper %s (%s logger)" % (configure.version, ripResult.logger)) # Rip date date = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(epoch)).strip() lines.append("Log creation date: %s" % date) lines.append("") # Rip technical settings lines.append("Ripping phase information:") lines.append(" Drive: %s%s (revision %s)" % (ripResult.vendor, ripResult.model, ripResult.release)) if ripResult.cdparanoiaDefeatsCache is None: defeat = "Unknown" elif ripResult.cdparanoiaDefeatsCache: defeat = "Yes" else: defeat = "No" lines.append(" Defeat audio cache: %s" % defeat) lines.append(" Read offset correction: %+d" % ripResult.offset) # Currently unsupported by the official cdparanoia package over = "No" # Only implemented in whipper (ripResult.overread) if ripResult.overread: over = "Yes" lines.append(" Overread into lead-out: %s" % over) # Next one fully works only using the patched cdparanoia package # lines.append("Fill up missing offset samples with silence: Yes") lines.append(" Gap detection: cdrdao %s" % ripResult.cdrdaoVersion) lines.append("") # Rip encoding settings lines.append("Encoding phase information:") lines.append(" Used output format: %s" % ripResult.profileName) lines.append(" GStreamer:") lines.append(" Pipeline: %s" % ripResult.profilePipeline) lines.append(" Version: %s" % ripResult.gstreamerVersion) lines.append(" Python version: %s" % ripResult.gstPythonVersion) lines.append(" Encoder plugin version: %s" % ripResult.encoderVersion) lines.append("") # CD metadata lines.append("CD metadata:") lines.append(" Album: %s - %s" % (ripResult.artist, ripResult.title)) lines.append(" CDDB Disc ID: %s" % ripResult.table.getCDDBDiscId()) lines.append(" MusicBrainz Disc ID: %s" % ripResult.table.getMusicBrainzDiscId()) lines.append(" MusicBrainz lookup url: %s" % ripResult.table.getMusicBrainzSubmitURL()) lines.append("") # TOC section lines.append("TOC:") table = ripResult.table # Test for HTOA presence htoa = None try: htoa = table.tracks[0].getIndex(0) except KeyError: pass # If True, include HTOA line into log's TOC if htoa and htoa.path: htoastart = htoa.absolute htoaend = table.getTrackEnd(0) htoalength = table.tracks[0].getIndex(1).absolute - htoastart lines.append(" 00:") lines.append(" Start: %s" % common.framesToMSF(htoastart)) lines.append(" Length: %s" % common.framesToMSF(htoalength)) lines.append(" Start sector: %d" % htoastart) lines.append(" End sector: %d" % htoaend) lines.append("") # For every track include information in the TOC for t in table.tracks: # FIXME: what happens to a track start over 60 minutes ? # Answer: tested empirically, everything seems OK start = t.getIndex(1).absolute length = table.getTrackLength(t.number) end = table.getTrackEnd(t.number) lines.append(" %02d:" % t.number) lines.append(" Start: %s" % common.framesToMSF(start)) lines.append(" Length: %s" % common.framesToMSF(length)) lines.append(" Start sector: %d" % start) lines.append(" End sector: %d" % end) lines.append("") # Tracks section lines.append("Tracks:") duration = 0.0 for t in ripResult.tracks: if not t.filename: continue lines.extend(self.trackLog(t)) lines.append("") duration += t.testduration + t.copyduration # Status report lines.append("Conclusive status report:") arHeading = " AccurateRip summary:" if self._inARDatabase == 0: lines.append("%s None of the tracks are present in the " "AccurateRip database" % arHeading) else: nonHTOA = len(ripResult.tracks) if ripResult.tracks[0].number == 0: nonHTOA -= 1 if self._accuratelyRipped == 0: lines.append("%s No tracks could be verified as accurate " "(you may have a different pressing from the " "one(s) in the database)" % arHeading) elif self._accuratelyRipped < nonHTOA: accurateTracks = nonHTOA - self._accuratelyRipped lines.append("%s Some tracks could not be verified as " "accurate (%d/%d got no match)" % (arHeading, accurateTracks, nonHTOA)) else: lines.append("%s All tracks accurately ripped" % arHeading) hsHeading = " Health status:" if self._errors: lines.append("%s There were errors" % hsHeading) else: lines.append("%s No errors occurred" % hsHeading) lines.append(" EOF: End of status report") lines.append("") # Log hash hasher = hashlib.sha256() hasher.update("\n".join(lines).encode("utf-8")) lines.append("SHA-256 hash: %s" % hasher.hexdigest().upper()) lines.append("") return lines
def trackLog(self, trackResult): """Returns Tracks section lines: data picked from trackResult""" lines = [] # Track number lines.append(" %02d:" % trackResult.number) # Filename (including path) of ripped track lines.append(" Filename: %s" % trackResult.filename) # Pre-gap length pregap = trackResult.pregap if pregap: lines.append(" Pre-gap length: %s" % common.framesToMSF(pregap)) # Peak level peak = trackResult.peak lines.append(" Peak level: %.6f" % peak) # Extraction speed if trackResult.copyspeed: lines.append(" Extraction speed: %.1f X" % (trackResult.copyspeed)) # Extraction quality if trackResult.quality and trackResult.quality > 0.001: lines.append(" Extraction quality: %.2f %%" % (trackResult.quality * 100.0, )) # Ripper Test CRC if trackResult.testcrc is not None: lines.append(" Test CRC: %08X" % trackResult.testcrc) # Ripper Copy CRC if trackResult.copycrc is not None: lines.append(" Copy CRC: %08X" % trackResult.copycrc) # AccurateRip track status # Currently there's no support for AccurateRip V2 if trackResult.accurip: lines.append(" AccurateRip V1:") self._inARDatabase += 1 if trackResult.ARCRC == trackResult.ARDBCRC: lines.append(" Result: Found, exact match") self._accuratelyRipped += 1 else: lines.append(" Result: Found, NO exact match") lines.append(" Confidence: %d" % trackResult.ARDBConfidence) lines.append(" Local CRC: %08X" % trackResult.ARCRC) lines.append(" Remote CRC: %08X" % trackResult.ARDBCRC) elif trackResult.number != 0: lines.append(" AccurateRip V1:") lines.append(" Result: Track not present in " "AccurateRip database") # Check if Test & Copy CRCs are equal if trackResult.testcrc == trackResult.copycrc: lines.append(" Status: Copy OK") else: self._errors = True lines.append(" Status: Error, CRC mismatch") return lines
def cue(self, program='Morituri'): """ Dump our internal representation to a .cue file content. """ lines = [] def writeFile(path): lines.append('FILE "%s" WAVE' % os.path.basename(path)) # header main = ['PERFORMER', 'TITLE'] for key in CDTEXT_FIELDS: if key not in main and key in self.cdtext: lines.append(" %s %s" % (key, self.cdtext[key])) assert self.hasTOC(), "Table does not represent a full CD TOC" lines.append('REM DISCID %s' % self.getCDDBDiscId().upper()) lines.append('REM COMMENT "%s"' % program) if self.catalog: lines.append("CATALOG %s" % self.catalog) for key in main: if key in self.cdtext: lines.append('%s "%s"' % (key, self.cdtext[key])) # add the first FILE line path = self.tracks[0].getFirstIndex().path counter = self.tracks[0].getFirstIndex().counter writeFile(path) for i, track in enumerate(self.tracks): # FIXME: skip data tracks for now if not track.audio: continue # if there is no index 0, but there is a new file, advance # FILE line here if not 0 in track.indexes: index = track.indexes[1] if index.counter != counter: writeFile(index.path) counter = index.counter lines.append(" TRACK %02d %s" % (i + 1, 'AUDIO')) for key in CDTEXT_FIELDS: if key in track.cdtext: lines.append(' %s "%s"' % (key, track.cdtext[key])) if track.isrc is not None: lines.append(" ISRC %s" % track.isrc) indexes = track.indexes.keys() indexes.sort() for number in indexes: index = track.indexes[number] if index.counter != counter: writeFile(index.path) counter = index.counter lines.append(" INDEX %02d %s" % (number, common.framesToMSF(index.relative))) lines.append("") return "\n".join(lines)
def logRip(self, ripResult, epoch): lines = [] ### global lines.append("morituri version %s" % configure.version) lines.append("") # FIXME: when we localize this, see #49 to handle unicode properly. import locale old = locale.getlocale(locale.LC_TIME) locale.setlocale(locale.LC_TIME, "C") date = time.strftime("%b %d %H:%M:%S", time.localtime(epoch)) locale.setlocale(locale.LC_TIME, old) lines.append("morituri logfile from %s" % date) lines.append("") # album lines.append("%s / %s" % (ripResult.artist, ripResult.title)) lines.append("") # drive lines.append("Used Drive : %s %s %s" % (ripResult.vendor, ripResult.model, ripResult.release)) lines.append("") # Default for cdparanoia lines.append("Use cdparanoia mode : Yes (%s)" % (ripResult.cdparanoia_version)) # Default for cdparanoia by virtue of ripping whole tracks at a time lines.append("Defeat audio cache : Yes") # Default for cdparanoia by virtue of having no C2 rip mode lines.append("Make use of C2 pointers : No") lines.append("") lines.append("Read offset correction : %d" % (ripResult.offset)) # Currently unsupported by cdparanoia lines.append("Overread into Lead-In and Lead-Out : No") # Default for cdparanoia lines.append("Fill up missing offset samples with silence : Yes") # Default for cdparanoia lines.append("Delete leading and trailing silent blocks : No") # Default lines.append("Null samples used in CRC calculations : Yes") lines.append("Gap Detection : " "cdrdao version %s" % ripResult.cdrdao_version) # Default for cdparanoia lines.append("Gap handling : " "Appended to previous track") lines.append("") # toc lines.append("TOC of the extracted CD") lines.append("") lines.append(" Track | Start | Length | Start sector | End sector") lines.append(" ---------------------------------------------------------") table = ripResult.table for t in table.tracks: start = t.getIndex(1).absolute length = table.getTrackLength(t.number) lines.append( " %2d | %s | %s | %9d | %8d" % (t.number, common.framesToMSF(start), common.framesToMSF(length), start, start + length - 1) ) lines.append("") lines.append("") ### per-track for t in ripResult.tracks: lines.extend(self.trackLog(t)) lines.append("") return lines