This allows log parsers to always extract correct information about the "artist" and "title" fields: previously that was a problem if either (or both) of those fields contained a "-" character. Fixes #416. Signed-off-by: JoeLametta <JoeLametta@users.noreply.github.com>
241 lines
8.9 KiB
Python
241 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 = "null"
|
|
elif ripResult.cdparanoiaDefeatsCache:
|
|
defeat = "true"
|
|
else:
|
|
defeat = "false"
|
|
lines.append(" Defeat audio cache: %s" % defeat)
|
|
lines.append(" Read offset correction: %+d" % ripResult.offset)
|
|
# Currently unsupported by the official cdparanoia package
|
|
over = "false"
|
|
# Only implemented in whipper (ripResult.overread)
|
|
if ripResult.overread:
|
|
over = "true"
|
|
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: true")
|
|
lines.append(" Gap detection: cdrdao %s" % ripResult.cdrdaoVersion)
|
|
|
|
isCdr = "true" if ripResult.isCdr else "false"
|
|
lines.append(" CD-R detected: %s" % isCdr)
|
|
lines.append("")
|
|
|
|
# CD metadata
|
|
lines.append("CD metadata:")
|
|
lines.append(" Release:")
|
|
lines.append(" Artist: %s" % ripResult.artist)
|
|
lines.append(" Title: %s" % 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())
|
|
if ripResult.metadata:
|
|
lines.append(" MusicBrainz Release URL: %s" %
|
|
ripResult.metadata.url)
|
|
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(" 0:")
|
|
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:
|
|
start = t.getIndex(1).absolute
|
|
length = table.getTrackLength(t.number)
|
|
end = table.getTrackEnd(t.number)
|
|
lines.append(" %d:" % 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(" %d:" % 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)
|
|
preEmph = "true" if trackResult.pre_emphasis else "false"
|
|
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)
|