The values stored in variables _inARDatabase and _accuratelyRipped were double incremented when both AR v1 and AR V2 entries were present e/o matched, leading to a wrong calculation and potentially erroneous summary reporting. After this fix, in case of double matches only one is counted against each track.
243 lines
8.9 KiB
Python
243 lines
8.9 KiB
Python
import time
|
|
import hashlib
|
|
|
|
import whipper
|
|
|
|
from whipper.common import common
|
|
from whipper.result import result
|
|
|
|
|
|
class WhipperLogger(result.Logger):
|
|
|
|
_accuratelyRipped = 0
|
|
_inARDatabase = 0
|
|
_errors = False
|
|
|
|
def log(self, ripResult, epoch=time.time()):
|
|
"""Returns big str: logfile joined text lines"""
|
|
|
|
lines = self.logRip(ripResult, epoch=epoch)
|
|
return "\n".join(lines)
|
|
|
|
def logRip(self, ripResult, epoch):
|
|
"""Returns logfile lines list"""
|
|
|
|
lines = []
|
|
|
|
# Ripper version
|
|
lines.append("Log created by: whipper %s (internal logger)" %
|
|
whipper.__version__)
|
|
|
|
# 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))
|
|
lines.append(" Extraction engine: cdparanoia %s" %
|
|
ripResult.cdparanoiaVersion)
|
|
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)
|
|
if ripResult.isCdr:
|
|
isCdr = "Yes"
|
|
else:
|
|
isCdr = "No"
|
|
lines.append(" CD-R detected: %s" % isCdr)
|
|
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
|
|
track_lines, ARDB_entry, ARDB_match = self.trackLog(t)
|
|
self._inARDatabase += int(ARDB_entry)
|
|
self._accuratelyRipped += int(ARDB_match)
|
|
lines.extend(track_lines)
|
|
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 / 32768.0
|
|
lines.append(" Peak level: %.6f" % peak)
|
|
|
|
# Pre-emphasis status
|
|
# Only implemented in whipper (trackResult.pre_emphasis)
|
|
if trackResult.pre_emphasis:
|
|
preEmph = "Yes"
|
|
else:
|
|
preEmph = "No"
|
|
lines.append(" Pre-emphasis: %s" % preEmph)
|
|
|
|
# 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
|
|
ARDB_entry = 0
|
|
ARDB_match = 0
|
|
for v in ("v1", "v2"):
|
|
if trackResult.AR[v]["DBCRC"]:
|
|
lines.append(" AccurateRip %s:" % v)
|
|
ARDB_entry += 1
|
|
if trackResult.AR[v]["CRC"] == trackResult.AR[v]["DBCRC"]:
|
|
lines.append(" Result: Found, exact match")
|
|
ARDB_match += 1
|
|
else:
|
|
lines.append(" Result: Found, NO exact match")
|
|
lines.append(
|
|
" Confidence: %d" % trackResult.AR[v]["DBConfidence"]
|
|
)
|
|
lines.append(
|
|
" Local CRC: %s" % trackResult.AR[v]["CRC"].upper()
|
|
)
|
|
lines.append(
|
|
" Remote CRC: %s" % trackResult.AR[v]["DBCRC"].upper()
|
|
)
|
|
elif trackResult.number != 0:
|
|
lines.append(" AccurateRip %s:" % v)
|
|
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, bool(ARDB_entry), bool(ARDB_match)
|