diff --git a/morituri/result/logger.py b/morituri/result/logger.py index fe4c911..17de137 100644 --- a/morituri/result/logger.py +++ b/morituri/result/logger.py @@ -1,26 +1,5 @@ -# -*- Mode: Python; test-case-name: morituri.test.test_result_logger -*- -# vi:si:et:sw=4:sts=4:ts=4 - -# Morituri - for those about to RIP - -# Copyright (C) 2009 Thomas Vander Stichele - -# This file is part of morituri. -# -# morituri is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# morituri is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with morituri. If not, see . - import time +import hashlib from morituri.common import common from morituri.configure import configure @@ -29,125 +8,191 @@ from morituri.result import result class MorituriLogger(result.Logger): + _accuratelyRipped = 0 + _inARDatabase = 0 + _errors = False + + def _framesToMSF(self, frames): + # format specifically for EAC log; examples (5:39.57) + f = frames % common.FRAMES_PER_SECOND + frames -= f + s = (frames / common.FRAMES_PER_SECOND) % 60 + frames -= s * 60 + m = frames / common.FRAMES_PER_SECOND / 60 + return "%2d:%02d.%02d" % (m, s, f) + + def _framesToHMSH(self, frames): + # format specifically for EAC log; examples (0:00.00.70) + f = frames % common.FRAMES_PER_SECOND + frames -= f + s = (frames / common.FRAMES_PER_SECOND) % 60 + frames -= s * 60 + m = frames / common.FRAMES_PER_SECOND / 60 + frames -= m * 60 + h = frames / common.FRAMES_PER_SECOND / 60 / 60 + return "%2d:%02d:%02d.%02d" % (h, m, s, f) + def log(self, ripResult, epoch=time.time()): - """ - @type ripResult: L{morituri.result.result.RipResult} - """ lines = self.logRip(ripResult, epoch=epoch) - return '\n'.join(lines) + return "\n".join(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("Ripper: morituri %s" % configure.version) + date = time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime(epoch)).strip() + lines.append("Ripped at: %s" % date) + lines.append("Drive: %s%s{%s}" % + (ripResult.vendor, ripResult.model, ripResult.release)) + defeat = "Unknown" + if ripResult.cdparanoiaDefeatsCache is True: + defeat = "Yes" + if ripResult.cdparanoiaDefeatsCache is False: + defeat = "No" + lines.append("Defeat audio cache: %s" % defeat) lines.append("") - # album - lines.append("Album: %s - %s" % (ripResult.artist, ripResult.title)) + lines.append("Read offset correction: %d" % ripResult.offset) + # Currently unsupported by the official cdparanoia package + lines.append("Overread: No") + # Fully working 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("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("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("") - # drive - lines.append( - "Drive: vendor %s, model %s" % ( - ripResult.vendor, ripResult.model)) - lines.append("") - - lines.append("Read offset correction: %d" % - ripResult.offset) - overread = "Unknown" - if ripResult.overread is True: - overread = "Yes (Lead-Out)" - if ripResult.overread is False: - overread = "No" - lines.append("Overread: %s" % overread) - lines.append("") - - # toc - lines.append("Table of Contents:") - lines.append("") - lines.append( - " Track | Start | Length") - 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 + 1 + lines.append(" 0:") + lines.append(" Start: %s" % self._framesToMSF(htoastart)) + lines.append(" Length: %s" % self._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 ? 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))) + end = table.getTrackEnd(t.number) + lines.append(" %d:" % t.number) + lines.append(" Start: %s" % self._framesToMSF(start)) + lines.append(" Length: %s" % self._framesToMSF(length)) + lines.append(" Start sector: %d" % start) + lines.append(" End sector: %d" % end) + lines.append("") - lines.append("") - lines.append("") - - ### per-track + lines.append("Tracks:") + duration = 0.0 for t in ripResult.tracks: + if not t.filename: + continue lines.extend(self.trackLog(t)) - lines.append('') + lines.append("") + duration += t.testduration + t.copyduration + lines.append("AccurateRip Summary:") + if self._inARDatabase == 0: + lines.append(" 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(" No tracks could be verified as accurate") + lines.append(" 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(" Some tracks could not be verified as accurate") + else: + lines.append(" All tracks accurately ripped") + lines.append("") + + lines.append("Errors:") + if self._errors: + lines.append(" There were errors") + else: + lines.append(" No errors occurred") + lines.append("") + lines.append("End of status report") + lines.append("") + + hasher = hashlib.sha256() + hasher.update("\n".join(lines).encode("utf-8")) + lines.append("==== Log checksum %s ====" % hasher.hexdigest()) + lines.append("") 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)) + lines.append(" %d:" % trackResult.number) + lines.append(" Filename: %s" % trackResult.filename) + # EAC adds the 2 seconds to the first track pregap + pregap = trackResult.pregap + # if trackResult.number == 1: + # pregap += 2 * common.FRAMES_PER_SECOND + if pregap: + lines.append(" Pre-gap length: %s" % self._framesToHMSH(pregap)) + # EAC seems to format peak differently, truncating to the 3rd digit, + # and also calculating it against a max of 32767 + # MBV - Feed me with your kiss: replaygain 0.809875, + # EAC's peak level 80.9 % instead of 90.0 % + peak = trackResult.peak + # lines.append(' Peak level %r' % peak) + lines.append(" Peak level: %.6f %%" % peak) + # level = "%.2f" % (trackResult.peak * 100.0) + # level = level[:-1] + # lines.append(' Peak level %s %%' % level) if trackResult.copyspeed: - lines.append(' Extraction Speed (Copy) %.4f X' % ( + lines.append(" Extraction speed: %.1f 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) + # Track quality is shown in secure mode + if trackResult.quality and trackResult.quality > 0.001: + lines.append(" Track quality: %.1f %%" % + (trackResult.quality * 100.0, )) 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") - - + lines.append(" Test CRC: %08X" % trackResult.testcrc) + if trackResult.copycrc is not None: + lines.append(" Copy CRC: %08X" % trackResult.copycrc) + lines.append(" AccurateRip v1:") 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)) + self._inARDatabase += 1 + if trackResult.ARCRC == trackResult.ARDBCRC: + lines.append(" Confidence: %d" % + trackResult.ARDBConfidence) + lines.append(" Checksum: %08X" % trackResult.ARCRC) + self._accuratelyRipped += 1 else: - lines.append(' Track not present in AccurateRip database') + lines.append(" Cannot be verified as accurate " + "(confidence %d), [%08X], " + "AccurateRip returned [%08x]" % ( + trackResult.ARDBConfidence, + trackResult.ARCRC, trackResult.ARDBCRC)) + else: + lines.append(" Track not present in AccurateRip database") + if trackResult.testcrc == trackResult.copycrc: + lines.append(" Copy OK") + else: + self._errors = True + lines.append(" Error: CRC mismatch!") return lines