Merge pull request #524 from blueblots/skip-error-tracks
Implemented the option (`-k`, `--keep-going`) to continue ripping the CD even if one track fails to rip Resolves #128
This commit is contained in:
@@ -69,6 +69,10 @@ Options
|
||||
| Number of rip attempts before giving up if can't rip a track. This
|
||||
| defaults to 5; 0 means infinity.
|
||||
|
||||
| **-k** | **--keep-going**
|
||||
| continue ripping further tracks instead of giving up if a track can't be
|
||||
| ripped
|
||||
|
||||
Template schemes
|
||||
================
|
||||
|
||||
|
||||
@@ -192,13 +192,13 @@ class _CD(BaseCommand):
|
||||
cdio.Device(self.device).get_hwinfo()
|
||||
self.program.result.metadata = self.program.metadata
|
||||
|
||||
self.doCommand()
|
||||
ret = self.doCommand()
|
||||
|
||||
if (self.options.eject == 'success' and self.eject or
|
||||
self.options.eject == 'always'):
|
||||
utils.eject_device(self.device)
|
||||
|
||||
return None
|
||||
return ret
|
||||
|
||||
def doCommand(self):
|
||||
pass
|
||||
@@ -220,6 +220,9 @@ class Info(_CD):
|
||||
class Rip(_CD):
|
||||
summary = "rip CD"
|
||||
# see whipper.common.program.Program.getPath for expansion
|
||||
skipped_tracks = []
|
||||
# this holds tracks that fail to rip -
|
||||
# currently only used when the --keep-going option is used
|
||||
description = """
|
||||
Rips a CD.
|
||||
|
||||
@@ -310,6 +313,11 @@ Log files will log the path to tracks relative to this directory.
|
||||
"{}; 0 means "
|
||||
"infinity.".format(DEFAULT_MAX_RETRIES),
|
||||
default=DEFAULT_MAX_RETRIES)
|
||||
self.parser.add_argument('-k', '--keep-going',
|
||||
action='store_true',
|
||||
help="continue ripping further tracks "
|
||||
"instead of giving up if a track "
|
||||
"can't be ripped")
|
||||
|
||||
def handle_arguments(self):
|
||||
self.options.output_directory = os.path.expanduser(
|
||||
@@ -476,18 +484,32 @@ Log files will log the path to tracks relative to this directory.
|
||||
tries -= 1
|
||||
logger.critical('giving up on track %d after %d times',
|
||||
number, tries)
|
||||
raise RuntimeError("track can't be ripped. "
|
||||
"Rip attempts number is equal to %d",
|
||||
self.options.max_retries)
|
||||
if trackResult.testcrc == trackResult.copycrc:
|
||||
logger.info('CRCs match for track %d', number)
|
||||
if self.options.keep_going:
|
||||
logger.warning("track %d failed to rip.", number)
|
||||
logger.debug("adding %s to skipped_tracks",
|
||||
trackResult)
|
||||
self.skipped_tracks.append(trackResult)
|
||||
logger.debug("skipped_tracks = %s",
|
||||
self.skipped_tracks)
|
||||
trackResult.skipped = True
|
||||
else:
|
||||
raise RuntimeError("track can't be ripped. "
|
||||
"Rip attempts number is equal "
|
||||
"to %d",
|
||||
self.options.max_retries)
|
||||
if trackResult in self.skipped_tracks:
|
||||
print("Skipping CRC comparison for track %d "
|
||||
"due to rip failure" % number)
|
||||
else:
|
||||
raise RuntimeError(
|
||||
"CRCs did not match for track %d" % number
|
||||
)
|
||||
if trackResult.testcrc == trackResult.copycrc:
|
||||
logger.info('CRCs match for track %d', number)
|
||||
else:
|
||||
raise RuntimeError(
|
||||
"CRCs did not match for track %d" % number
|
||||
)
|
||||
|
||||
print('Peak level: %.6f' % (trackResult.peak / 32768.0))
|
||||
print('Rip quality: {:.2%}'.format(trackResult.quality))
|
||||
print('Peak level: %.6f' % (trackResult.peak / 32768.0))
|
||||
print('Rip quality: {:.2%}'.format(trackResult.quality))
|
||||
|
||||
# overlay this rip onto the Table
|
||||
if number == 0:
|
||||
@@ -507,7 +529,8 @@ Log files will log the path to tracks relative to this directory.
|
||||
self.itable.getTrackStart(1), number)
|
||||
else:
|
||||
self.itable.setFile(number, 1, trackResult.filename,
|
||||
self.itable.getTrackLength(number), number)
|
||||
self.itable.getTrackLength(number),
|
||||
number)
|
||||
|
||||
# check for hidden track one audio
|
||||
htoa = self.program.getHTOA()
|
||||
@@ -542,6 +565,12 @@ Log files will log the path to tracks relative to this directory.
|
||||
logger.debug('writing m3u file for %r', discName)
|
||||
self.program.write_m3u(discName)
|
||||
|
||||
if len(self.skipped_tracks) > 0:
|
||||
logger.warning("the generated cue sheet references %d track(s) "
|
||||
"which failed to rip so the associated file(s) "
|
||||
"won't be available", len(self.skipped_tracks))
|
||||
self.program.skipped_tracks = self.skipped_tracks
|
||||
|
||||
try:
|
||||
self.program.verifyImage(self.runner, self.itable)
|
||||
except accurip.EntryNotFound:
|
||||
@@ -551,6 +580,11 @@ Log files will log the path to tracks relative to this directory.
|
||||
|
||||
self.program.writeLog(discName, self.logger)
|
||||
|
||||
if len(self.skipped_tracks) > 0:
|
||||
logger.warning('%d tracks have been skipped from this rip attempt',
|
||||
len(self.skipped_tracks))
|
||||
return 5
|
||||
|
||||
|
||||
class CD(BaseCommand):
|
||||
summary = "handle CDs"
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
import struct
|
||||
import whipper
|
||||
import os
|
||||
from urllib.error import URLError, HTTPError
|
||||
from urllib.request import urlopen, Request
|
||||
|
||||
@@ -111,7 +112,11 @@ def calculate_checksums(track_paths):
|
||||
logger.debug('checksumming %d tracks', track_count)
|
||||
# This is done sequentially because it is very fast.
|
||||
for i, path in enumerate(track_paths):
|
||||
v1_sum, v2_sum = accuraterip_checksum(path, i+1, track_count)
|
||||
if os.path.exists(path):
|
||||
v1_sum, v2_sum = accuraterip_checksum(path, i+1, track_count)
|
||||
else:
|
||||
logger.warning('Can\'t checksum %s; path doesn\'t exist', path)
|
||||
v1_sum, v2_sum = None, None
|
||||
if v1_sum is None:
|
||||
logger.error('could not calculate AccurateRip v1 checksum '
|
||||
'for track %d %r', i + 1, path)
|
||||
|
||||
@@ -57,6 +57,7 @@ class Program:
|
||||
metadata = None
|
||||
outdir = None
|
||||
result = None
|
||||
skipped_tracks = None
|
||||
|
||||
def __init__(self, config, record=False):
|
||||
"""
|
||||
@@ -612,7 +613,12 @@ class Program:
|
||||
"""
|
||||
cueImage = image.Image(self.cuePath)
|
||||
# assigns track lengths
|
||||
verifytask = image.ImageVerifyTask(cueImage)
|
||||
if self.skipped_tracks is not None:
|
||||
verifytask = image.ImageVerifyTask(cueImage,
|
||||
[os.path.basename(t.filename)
|
||||
for t in self.skipped_tracks])
|
||||
else:
|
||||
verifytask = image.ImageVerifyTask(cueImage)
|
||||
runner.run(verifytask)
|
||||
if verifytask.exception:
|
||||
logger.error(verifytask.exceptionMessage)
|
||||
@@ -627,6 +633,7 @@ class Program:
|
||||
])
|
||||
if not (checksums and any(checksums['v1']) and any(checksums['v2'])):
|
||||
return False
|
||||
|
||||
return accurip.verify_result(self.result, responses, checksums)
|
||||
|
||||
def write_m3u(self, discname):
|
||||
@@ -637,6 +644,8 @@ class Program:
|
||||
if not track.filename:
|
||||
# false positive htoa
|
||||
continue
|
||||
if track.skipped:
|
||||
continue
|
||||
if track.number == 0:
|
||||
length = (self.result.table.getTrackStart(1) /
|
||||
common.FRAMES_PER_SECOND)
|
||||
|
||||
@@ -120,7 +120,7 @@ class ImageVerifyTask(task.MultiSeparateTask):
|
||||
description = "Checking tracks"
|
||||
lengths = None
|
||||
|
||||
def __init__(self, image):
|
||||
def __init__(self, image, skipped_tracks=[]):
|
||||
task.MultiSeparateTask.__init__(self)
|
||||
|
||||
self._image = image
|
||||
@@ -147,7 +147,17 @@ class ImageVerifyTask(task.MultiSeparateTask):
|
||||
length = cue.getTrackLength(track)
|
||||
|
||||
if length == -1:
|
||||
path = image.getRealPath(index.path)
|
||||
try:
|
||||
path = image.getRealPath(index.path)
|
||||
except KeyError:
|
||||
logger.debug('Path not found; Checking '
|
||||
'if %s is a skipped track', index.path)
|
||||
if index.path in skipped_tracks:
|
||||
logger.warning('Missing file %s due to skipped track',
|
||||
index.path)
|
||||
continue
|
||||
else:
|
||||
raise
|
||||
assert isinstance(path, str), "%r is not str" % path
|
||||
logger.debug('schedule scan of audio length of %r', path)
|
||||
taskk = AudioLengthTask(path)
|
||||
|
||||
@@ -15,6 +15,7 @@ class WhipperLogger(result.Logger):
|
||||
_accuratelyRipped = 0
|
||||
_inARDatabase = 0
|
||||
_errors = False
|
||||
_skippedTracks = False
|
||||
|
||||
def log(self, ripResult, epoch=time.time()):
|
||||
"""Return logfile as string."""
|
||||
@@ -139,6 +140,8 @@ class WhipperLogger(result.Logger):
|
||||
|
||||
if self._errors:
|
||||
message = "There were errors"
|
||||
elif self._skippedTracks:
|
||||
message = "Some tracks were not ripped (skipped)"
|
||||
else:
|
||||
message = "No errors occurred"
|
||||
data["Health status"] = message
|
||||
@@ -242,8 +245,12 @@ class WhipperLogger(result.Logger):
|
||||
data["Result"] = "Track not present in AccurateRip database"
|
||||
track["AccurateRip %s" % v] = data
|
||||
|
||||
# Check if track has been skipped
|
||||
if trackResult.skipped:
|
||||
track["Status"] = "Track not ripped (skipped)"
|
||||
self._skippedTracks = True
|
||||
# Check if Test & Copy CRCs are equal
|
||||
if trackResult.testcrc == trackResult.copycrc:
|
||||
elif trackResult.testcrc == trackResult.copycrc:
|
||||
track["Status"] = "Copy OK"
|
||||
else:
|
||||
self._errors = True
|
||||
|
||||
@@ -38,6 +38,7 @@ class TrackResult:
|
||||
copycrc = None
|
||||
AR = None
|
||||
classVersion = 3
|
||||
skipped = False
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user