Skip to content

Commit

Permalink
Merge pull request #524 from blueblots/skip-error-tracks
Browse files Browse the repository at this point in the history
Implemented the option (`-k`, `--keep-going`) to continue ripping the CD even if one track fails to rip

Resolves #128
  • Loading branch information
JoeLametta authored Mar 28, 2021
2 parents 87f3d00 + 2c8acd6 commit 7691a51
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 18 deletions.
4 changes: 4 additions & 0 deletions man/whipper-cd-rip.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
================

Expand Down
60 changes: 47 additions & 13 deletions whipper/command/cd.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,13 +192,13 @@ def do(self):
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
Expand All @@ -220,6 +220,9 @@ def add_arguments(self):
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.
Expand Down Expand Up @@ -310,6 +313,11 @@ def add_arguments(self):
"{}; 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(
Expand Down Expand Up @@ -476,18 +484,32 @@ def _ripIfNotRipped(number):
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:
Expand All @@ -507,7 +529,8 @@ def _ripIfNotRipped(number):
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()
Expand Down Expand Up @@ -542,6 +565,12 @@ def _ripIfNotRipped(number):
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:
Expand All @@ -551,6 +580,11 @@ def _ripIfNotRipped(number):

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"
Expand Down
7 changes: 6 additions & 1 deletion whipper/common/accurip.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import struct
import whipper
import os
from urllib.error import URLError, HTTPError
from urllib.request import urlopen, Request

Expand Down Expand Up @@ -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)
Expand Down
11 changes: 10 additions & 1 deletion whipper/common/program.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class Program:
metadata = None
outdir = None
result = None
skipped_tracks = None

def __init__(self, config, record=False):
"""
Expand Down Expand Up @@ -612,7 +613,12 @@ def verifyImage(self, runner, table):
"""
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)
Expand All @@ -627,6 +633,7 @@ def verifyImage(self, runner, table):
])
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):
Expand All @@ -637,6 +644,8 @@ def write_m3u(self, discname):
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)
Expand Down
14 changes: 12 additions & 2 deletions whipper/image/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -147,7 +147,17 @@ def __init__(self, image):
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)
Expand Down
9 changes: 8 additions & 1 deletion whipper/result/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand Down Expand Up @@ -139,6 +140,8 @@ def logRip(self, ripResult, epoch):

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
Expand Down Expand Up @@ -242,8 +245,12 @@ def trackLog(self, trackResult):
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
Expand Down
1 change: 1 addition & 0 deletions whipper/result/result.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class TrackResult:
copycrc = None
AR = None
classVersion = 3
skipped = False

def __init__(self):
"""
Expand Down

0 comments on commit 7691a51

Please sign in to comment.