Solve all flake8 warnings (#163)
Whipper is now fully PEP8 compliant. Revised version which includes all the changes suggested by Freso.
This commit is contained in:
@@ -14,7 +14,7 @@ doc = handle.read()
|
||||
|
||||
soup = BeautifulSoup.BeautifulSoup(doc)
|
||||
|
||||
offsets = {} # offset -> total count
|
||||
offsets = {} # offset -> total count
|
||||
|
||||
rows = soup.findAll('tr')
|
||||
for row in rows:
|
||||
|
||||
@@ -26,6 +26,7 @@ from whipper.common import accurip
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Show(BaseCommand):
|
||||
summary = "show accuraterip data"
|
||||
description = """
|
||||
@@ -52,7 +53,6 @@ retrieves and display accuraterip data from the given URL
|
||||
"Warning: response %d has %d tracks instead of %d\n" % (
|
||||
i, r.trackCount, count))
|
||||
|
||||
|
||||
# checksum and confidence by track
|
||||
for track in range(count):
|
||||
sys.stdout.write("Track %d:\n" % (track + 1))
|
||||
|
||||
@@ -24,6 +24,7 @@ logger = logging.getLogger(__name__)
|
||||
# A: The prefix matching prevents passing '-h' (and possibly other
|
||||
# options) to the child command.
|
||||
|
||||
|
||||
class BaseCommand():
|
||||
"""
|
||||
A base command class for whipper commands.
|
||||
|
||||
@@ -24,10 +24,8 @@ import glob
|
||||
import urllib2
|
||||
import socket
|
||||
import sys
|
||||
|
||||
import logging
|
||||
import gobject
|
||||
gobject.threads_init()
|
||||
|
||||
from whipper.command.basecommand import BaseCommand
|
||||
from whipper.common import (
|
||||
accurip, common, config, drive, program, task
|
||||
@@ -35,7 +33,8 @@ from whipper.common import (
|
||||
from whipper.program import cdrdao, cdparanoia, utils
|
||||
from whipper.result import result
|
||||
|
||||
import logging
|
||||
gobject.threads_init()
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -81,24 +80,26 @@ class _CD(BaseCommand):
|
||||
def add_arguments(parser):
|
||||
# FIXME: have a cache of these pickles somewhere
|
||||
parser.add_argument('-T', '--toc-pickle',
|
||||
action="store", dest="toc_pickle",
|
||||
help="pickle to use for reading and writing the TOC")
|
||||
action="store", dest="toc_pickle",
|
||||
help="pickle to use for reading and "
|
||||
"writing the TOC")
|
||||
parser.add_argument('-R', '--release-id',
|
||||
action="store", dest="release_id",
|
||||
help="MusicBrainz release id to match to (if there are multiple)")
|
||||
action="store", dest="release_id",
|
||||
help="MusicBrainz release id to match to "
|
||||
"(if there are multiple)")
|
||||
parser.add_argument('-p', '--prompt',
|
||||
action="store_true", dest="prompt",
|
||||
help="Prompt if there are multiple matching releases")
|
||||
action="store_true", dest="prompt",
|
||||
help="Prompt if there are multiple "
|
||||
"matching releases")
|
||||
parser.add_argument('-c', '--country',
|
||||
action="store", dest="country",
|
||||
help="Filter releases by country")
|
||||
|
||||
action="store", dest="country",
|
||||
help="Filter releases by country")
|
||||
|
||||
def do(self):
|
||||
self.config = config.Config()
|
||||
self.program = program.Program(self.config,
|
||||
record=self.options.record,
|
||||
stdout=sys.stdout)
|
||||
record=self.options.record,
|
||||
stdout=sys.stdout)
|
||||
self.runner = task.SyncRunner()
|
||||
|
||||
# if the device is mounted (data session), unmount it
|
||||
@@ -110,8 +111,8 @@ class _CD(BaseCommand):
|
||||
|
||||
# first, read the normal TOC, which is fast
|
||||
self.ittoc = self.program.getFastToc(self.runner,
|
||||
self.options.toc_pickle,
|
||||
self.device)
|
||||
self.options.toc_pickle,
|
||||
self.device)
|
||||
|
||||
# already show us some info based on this
|
||||
self.program.getRipResult(self.ittoc.getCDDBDiscId())
|
||||
@@ -120,13 +121,14 @@ class _CD(BaseCommand):
|
||||
sys.stdout.write("MusicBrainz disc id %s\n" % self.mbdiscid)
|
||||
|
||||
sys.stdout.write("MusicBrainz lookup URL %s\n" %
|
||||
self.ittoc.getMusicBrainzSubmitURL())
|
||||
self.ittoc.getMusicBrainzSubmitURL())
|
||||
|
||||
self.program.metadata = self.program.getMusicBrainz(self.ittoc,
|
||||
self.mbdiscid,
|
||||
release=self.options.release_id,
|
||||
country=self.options.country,
|
||||
prompt=self.options.prompt)
|
||||
self.program.metadata = (
|
||||
self.program.getMusicBrainz(self.ittoc, self.mbdiscid,
|
||||
release=self.options.release_id,
|
||||
country=self.options.country,
|
||||
prompt=self.options.prompt)
|
||||
)
|
||||
|
||||
if not self.program.metadata:
|
||||
# fall back to FreeDB for lookup
|
||||
@@ -153,8 +155,9 @@ class _CD(BaseCommand):
|
||||
|
||||
# now, read the complete index table, which is slower
|
||||
self.itable = self.program.getTable(self.runner,
|
||||
self.ittoc.getCDDBDiscId(),
|
||||
self.ittoc.getMusicBrainzDiscId(), self.device, offset)
|
||||
self.ittoc.getCDDBDiscId(),
|
||||
self.ittoc.getMusicBrainzDiscId(),
|
||||
self.device, offset)
|
||||
|
||||
assert self.itable.getCDDBDiscId() == self.ittoc.getCDDBDiscId(), \
|
||||
"full table's id %s differs from toc id %s" % (
|
||||
@@ -220,6 +223,7 @@ class Info(_CD):
|
||||
def add_arguments(self):
|
||||
_CD.add_arguments(self.parser)
|
||||
|
||||
|
||||
class Rip(_CD):
|
||||
summary = "rip CD"
|
||||
# see whipper.common.program.Program.getPath for expansion
|
||||
@@ -247,49 +251,62 @@ Log files will log the path to tracks relative to this directory.
|
||||
try:
|
||||
default_offset = config.Config().getReadOffset(*info)
|
||||
sys.stdout.write("Using configured read offset %d\n" %
|
||||
default_offset)
|
||||
default_offset)
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
_CD.add_arguments(self.parser)
|
||||
|
||||
self.parser.add_argument('-L', '--logger',
|
||||
action="store", dest="logger", default='whipper',
|
||||
help="logger to use (choose from '" + "', '".join(loggers) + "')")
|
||||
action="store", dest="logger",
|
||||
default='whipper',
|
||||
help="logger to use (choose from '"
|
||||
"', '".join(loggers) + "')")
|
||||
# FIXME: get from config
|
||||
self.parser.add_argument('-o', '--offset',
|
||||
action="store", dest="offset", default=default_offset,
|
||||
help="sample read offset")
|
||||
action="store", dest="offset",
|
||||
default=default_offset,
|
||||
help="sample read offset")
|
||||
self.parser.add_argument('-x', '--force-overread',
|
||||
action="store_true", dest="overread", default=False,
|
||||
help="Force overreading into the lead-out portion of the disc. "
|
||||
"Works only if the patched cdparanoia package is installed "
|
||||
"and the drive supports this feature. ")
|
||||
action="store_true", dest="overread",
|
||||
default=False,
|
||||
help="Force overreading into the "
|
||||
"lead-out portion of the disc. Works only "
|
||||
"if the patched cdparanoia package is "
|
||||
"installed and the drive "
|
||||
"supports this feature. ")
|
||||
self.parser.add_argument('-O', '--output-directory',
|
||||
action="store", dest="output_directory",
|
||||
default=os.path.relpath(os.getcwd()),
|
||||
help="output directory; will be included in file paths in log")
|
||||
action="store", dest="output_directory",
|
||||
default=os.path.relpath(os.getcwd()),
|
||||
help="output directory; will be included "
|
||||
"in file paths in log")
|
||||
self.parser.add_argument('-W', '--working-directory',
|
||||
action="store", dest="working_directory",
|
||||
help="working directory; whipper will change to this directory "
|
||||
"and files will be created relative to it when not absolute")
|
||||
action="store", dest="working_directory",
|
||||
help="working directory; whipper will "
|
||||
"change to this directory "
|
||||
"and files will be created relative to "
|
||||
"it when not absolute")
|
||||
self.parser.add_argument('--track-template',
|
||||
action="store", dest="track_template",
|
||||
default=DEFAULT_TRACK_TEMPLATE,
|
||||
help="template for track file naming (default default)")
|
||||
action="store", dest="track_template",
|
||||
default=DEFAULT_TRACK_TEMPLATE,
|
||||
help="template for track file naming "
|
||||
"(default default)")
|
||||
self.parser.add_argument('--disc-template',
|
||||
action="store", dest="disc_template",
|
||||
default=DEFAULT_DISC_TEMPLATE,
|
||||
help="template for disc file naming (default default)")
|
||||
action="store", dest="disc_template",
|
||||
default=DEFAULT_DISC_TEMPLATE,
|
||||
help="template for disc file naming "
|
||||
"(default default)")
|
||||
self.parser.add_argument('-U', '--unknown',
|
||||
action="store_true", dest="unknown",
|
||||
help="whether to continue ripping if the CD is unknown",
|
||||
default=False)
|
||||
action="store_true", dest="unknown",
|
||||
help="whether to continue ripping if "
|
||||
"the CD is unknown", default=False)
|
||||
|
||||
def handle_arguments(self):
|
||||
self.options.output_directory = os.path.expanduser(self.options.output_directory)
|
||||
self.options.output_directory = os.path.expanduser(
|
||||
self.options.output_directory)
|
||||
|
||||
self.options.track_template = self.options.track_template.decode('utf-8')
|
||||
self.options.track_template = self.options.track_template.decode(
|
||||
'utf-8')
|
||||
self.options.disc_template = self.options.disc_template.decode('utf-8')
|
||||
|
||||
if self.options.offset is None:
|
||||
@@ -300,9 +317,9 @@ Log files will log the path to tracks relative to this directory.
|
||||
"also be specified at runtime using the "
|
||||
"'--offset=value' argument")
|
||||
|
||||
|
||||
if self.options.working_directory is not None:
|
||||
self.options.working_directory = os.path.expanduser(self.options.working_directory)
|
||||
self.options.working_directory = os.path.expanduser(
|
||||
self.options.working_directory)
|
||||
|
||||
if self.options.logger:
|
||||
try:
|
||||
@@ -312,7 +329,6 @@ Log files will log the path to tracks relative to this directory.
|
||||
logger.critical(msg)
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
def doCommand(self):
|
||||
self.program.setWorkingDirectory(self.options.working_directory)
|
||||
self.program.outdir = self.options.output_directory.decode('utf-8')
|
||||
@@ -320,16 +336,17 @@ Log files will log the path to tracks relative to this directory.
|
||||
self.program.result.overread = self.options.overread
|
||||
self.program.result.logger = self.options.logger
|
||||
|
||||
### write disc files
|
||||
# write disc files
|
||||
disambiguate = False
|
||||
while True:
|
||||
discName = self.program.getPath(self.program.outdir,
|
||||
self.options.disc_template, self.mbdiscid, 0,
|
||||
disambiguate=disambiguate)
|
||||
self.options.disc_template,
|
||||
self.mbdiscid, 0,
|
||||
disambiguate=disambiguate)
|
||||
dirname = os.path.dirname(discName)
|
||||
if os.path.exists(dirname):
|
||||
sys.stdout.write("Output directory %s already exists\n" %
|
||||
dirname.encode('utf-8'))
|
||||
dirname.encode('utf-8'))
|
||||
logs = glob.glob(os.path.join(dirname, '*.log'))
|
||||
if logs:
|
||||
sys.stdout.write(
|
||||
@@ -344,14 +361,13 @@ Log files will log the path to tracks relative to this directory.
|
||||
|
||||
else:
|
||||
sys.stdout.write("Creating output directory %s\n" %
|
||||
dirname.encode('utf-8'))
|
||||
dirname.encode('utf-8'))
|
||||
os.makedirs(dirname)
|
||||
break
|
||||
|
||||
# FIXME: say when we're continuing a rip
|
||||
# FIXME: disambiguate if the pre-existing rip is different
|
||||
|
||||
|
||||
# FIXME: turn this into a method
|
||||
|
||||
def ripIfNotRipped(number):
|
||||
@@ -363,12 +379,12 @@ Log files will log the path to tracks relative to this directory.
|
||||
self.program.result.tracks.append(trackResult)
|
||||
else:
|
||||
logger.debug('ripIfNotRipped have trackresult, path %r' %
|
||||
trackResult.filename)
|
||||
trackResult.filename)
|
||||
|
||||
path = self.program.getPath(self.program.outdir,
|
||||
self.options.track_template,
|
||||
self.mbdiscid, number,
|
||||
disambiguate=disambiguate) \
|
||||
self.options.track_template,
|
||||
self.mbdiscid, number,
|
||||
disambiguate=disambiguate) \
|
||||
+ '.' + 'flac'
|
||||
logger.debug('ripIfNotRipped: path %r' % path)
|
||||
trackResult.number = number
|
||||
@@ -378,7 +394,9 @@ Log files will log the path to tracks relative to this directory.
|
||||
if number > 0:
|
||||
trackResult.pregap = self.itable.tracks[number - 1].getPregap()
|
||||
|
||||
trackResult.pre_emphasis = self.itable.tracks[number - 1].pre_emphasis
|
||||
trackResult.pre_emphasis = (
|
||||
self.itable.tracks[number - 1].pre_emphasis
|
||||
)
|
||||
|
||||
# FIXME: optionally allow overriding reripping
|
||||
if os.path.exists(path):
|
||||
@@ -411,19 +429,21 @@ Log files will log the path to tracks relative to this directory.
|
||||
os.path.basename(path).encode('utf-8')))
|
||||
try:
|
||||
logger.debug('ripIfNotRipped: track %d, try %d',
|
||||
number, tries)
|
||||
number, tries)
|
||||
self.program.ripTrack(self.runner, trackResult,
|
||||
offset=int(self.options.offset),
|
||||
device=self.device,
|
||||
taglist=self.program.getTagList(number),
|
||||
overread=self.options.overread,
|
||||
what='track %d of %d%s' % (
|
||||
number, len(self.itable.tracks), extra))
|
||||
offset=int(self.options.offset),
|
||||
device=self.device,
|
||||
taglist=self.program.getTagList(
|
||||
number),
|
||||
overread=self.options.overread,
|
||||
what='track %d of %d%s' % (
|
||||
number,
|
||||
len(self.itable.tracks),
|
||||
extra))
|
||||
break
|
||||
except Exception, e:
|
||||
logger.debug('Got exception %r on try %d',
|
||||
e, tries)
|
||||
|
||||
e, tries)
|
||||
|
||||
if tries == MAX_TRIES:
|
||||
logger.critical('Giving up on track %d after %d times' % (
|
||||
@@ -433,39 +453,43 @@ Log files will log the path to tracks relative to this directory.
|
||||
"Rip attempts number is equal to 'MAX_TRIES'")
|
||||
if trackResult.testcrc == trackResult.copycrc:
|
||||
sys.stdout.write('Checksums match for track %d\n' %
|
||||
number)
|
||||
number)
|
||||
else:
|
||||
sys.stdout.write(
|
||||
'ERROR: checksums did not match for track %d\n' %
|
||||
number)
|
||||
raise
|
||||
|
||||
sys.stdout.write('Peak level: {:.2%} \n'.format(trackResult.peak))
|
||||
sys.stdout.write(
|
||||
'Peak level: {:.2%} \n'.format(trackResult.peak))
|
||||
|
||||
sys.stdout.write('Rip quality: {:.2%}\n'.format(trackResult.quality))
|
||||
sys.stdout.write(
|
||||
'Rip quality: {:.2%}\n'.format(trackResult.quality))
|
||||
|
||||
# overlay this rip onto the Table
|
||||
if number == 0:
|
||||
# HTOA goes on index 0 of track 1
|
||||
# ignore silence in PREGAP
|
||||
if trackResult.peak <= SILENT:
|
||||
logger.debug('HTOA peak %r is below SILENT threshold, disregarding', trackResult.peak)
|
||||
logger.debug(
|
||||
'HTOA peak %r is below SILENT '
|
||||
'threshold, disregarding', trackResult.peak)
|
||||
self.itable.setFile(1, 0, None,
|
||||
self.ittoc.getTrackStart(1), number)
|
||||
self.ittoc.getTrackStart(1), number)
|
||||
logger.debug('Unlinking %r', trackResult.filename)
|
||||
os.unlink(trackResult.filename)
|
||||
trackResult.filename = None
|
||||
sys.stdout.write('HTOA discarded, contains digital silence\n')
|
||||
sys.stdout.write(
|
||||
'HTOA discarded, contains digital silence\n')
|
||||
else:
|
||||
self.itable.setFile(1, 0, trackResult.filename,
|
||||
self.ittoc.getTrackStart(1), number)
|
||||
self.ittoc.getTrackStart(1), number)
|
||||
else:
|
||||
self.itable.setFile(number, 1, trackResult.filename,
|
||||
self.ittoc.getTrackLength(number), number)
|
||||
self.ittoc.getTrackLength(number), number)
|
||||
|
||||
self.program.saveRipResult()
|
||||
|
||||
|
||||
# check for hidden track one audio
|
||||
htoapath = None
|
||||
htoa = self.program.getHTOA()
|
||||
@@ -473,7 +497,7 @@ Log files will log the path to tracks relative to this directory.
|
||||
start, stop = htoa
|
||||
sys.stdout.write(
|
||||
'Found Hidden Track One Audio from frame %d to %d\n' % (
|
||||
start, stop))
|
||||
start, stop))
|
||||
|
||||
# rip it
|
||||
ripIfNotRipped(0)
|
||||
@@ -484,17 +508,18 @@ Log files will log the path to tracks relative to this directory.
|
||||
if not track.audio:
|
||||
sys.stdout.write(
|
||||
'WARNING: skipping data track %d, not implemented\n' % (
|
||||
i + 1, ))
|
||||
i + 1, ))
|
||||
# FIXME: make it work for now
|
||||
track.indexes[1].relative = 0
|
||||
continue
|
||||
|
||||
ripIfNotRipped(i + 1)
|
||||
|
||||
### write disc files
|
||||
# write disc files
|
||||
discName = self.program.getPath(self.program.outdir,
|
||||
self.options.disc_template, self.mbdiscid, 0,
|
||||
disambiguate=disambiguate)
|
||||
self.options.disc_template,
|
||||
self.mbdiscid, 0,
|
||||
disambiguate=disambiguate)
|
||||
dirname = os.path.dirname(discName)
|
||||
if not os.path.exists(dirname):
|
||||
os.makedirs(dirname)
|
||||
@@ -516,20 +541,22 @@ Log files will log the path to tracks relative to this directory.
|
||||
u = '%s\n' % targetPath
|
||||
handle.write(u.encode('utf-8'))
|
||||
|
||||
|
||||
if htoapath:
|
||||
writeFile(handle, htoapath,
|
||||
self.itable.getTrackStart(1) / common.FRAMES_PER_SECOND)
|
||||
self.itable.getTrackStart(1) / common.FRAMES_PER_SECOND)
|
||||
|
||||
for i, track in enumerate(self.itable.tracks):
|
||||
if not track.audio:
|
||||
continue
|
||||
|
||||
path = self.program.getPath(self.program.outdir,
|
||||
self.options.track_template, self.mbdiscid, i + 1,
|
||||
disambiguate=disambiguate) + '.' + 'flac'
|
||||
self.options.track_template,
|
||||
self.mbdiscid, i + 1,
|
||||
disambiguate=disambiguate
|
||||
) + '.' + 'flac'
|
||||
writeFile(handle, path,
|
||||
self.itable.getTrackLength(i + 1) / common.FRAMES_PER_SECOND)
|
||||
(self.itable.getTrackLength(i + 1) /
|
||||
common.FRAMES_PER_SECOND))
|
||||
|
||||
handle.close()
|
||||
|
||||
@@ -556,14 +583,13 @@ Log files will log the path to tracks relative to this directory.
|
||||
|
||||
if responses:
|
||||
sys.stdout.write('%d AccurateRip reponses found\n' %
|
||||
len(responses))
|
||||
len(responses))
|
||||
|
||||
if responses[0].cddbDiscId != self.itable.getCDDBDiscId():
|
||||
sys.stdout.write(
|
||||
"AccurateRip response discid different: %s\n" %
|
||||
responses[0].cddbDiscId)
|
||||
|
||||
|
||||
self.program.verifyImage(self.runner, responses)
|
||||
|
||||
sys.stdout.write("\n".join(
|
||||
|
||||
@@ -28,6 +28,7 @@ from whipper.result import result
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class RCCue(BaseCommand):
|
||||
summary = "write a cue file for the cached result"
|
||||
description = summary
|
||||
@@ -148,9 +149,6 @@ class Encode(BaseCommand):
|
||||
description = summary
|
||||
|
||||
def add_arguments(self):
|
||||
# here to avoid import gst eating our options
|
||||
from whipper.common import encode
|
||||
|
||||
self.parser.add_argument('input', action='store',
|
||||
help="audio file to encode")
|
||||
self.parser.add_argument('output', nargs='?', action='store',
|
||||
@@ -174,8 +172,8 @@ class Encode(BaseCommand):
|
||||
runner = task.SyncRunner()
|
||||
|
||||
logger.debug('Encoding %s to %s',
|
||||
fromPath.encode('utf-8'),
|
||||
toPath.encode('utf-8'))
|
||||
fromPath.encode('utf-8'),
|
||||
toPath.encode('utf-8'))
|
||||
encodetask = encode.FlacEncodeTask(fromPath, toPath)
|
||||
|
||||
runner.run(encodetask)
|
||||
@@ -244,7 +242,7 @@ Example disc id: KnpGsLhvH.lPrNc1PBL21lb9Bg4-"""
|
||||
sys.stdout.write('- Release %d:\n' % (i + 1, ))
|
||||
sys.stdout.write(' Artist: %s\n' % md.artist.encode('utf-8'))
|
||||
sys.stdout.write(' Title: %s\n' % md.title.encode('utf-8'))
|
||||
sys.stdout.write(' Type: %s\n' % md.releaseType.encode('utf-8'))
|
||||
sys.stdout.write(' Type: %s\n' % md.releaseType.encode('utf-8')) # noqa: E501
|
||||
sys.stdout.write(' URL: %s\n' % md.url)
|
||||
sys.stdout.write(' Tracks: %d\n' % len(md.tracks))
|
||||
if md.catalogNumber:
|
||||
|
||||
@@ -28,9 +28,10 @@ from whipper.program import cdparanoia
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Analyze(BaseCommand):
|
||||
summary = "analyze caching behaviour of drive"
|
||||
description = """Determine whether cdparanoia can defeat the audio cache of the drive."""
|
||||
description = """Determine whether cdparanoia can defeat the audio cache of the drive.""" # noqa: E501
|
||||
device_option = True
|
||||
|
||||
def do(self):
|
||||
@@ -51,14 +52,15 @@ class Analyze(BaseCommand):
|
||||
|
||||
info = drive.getDeviceInfo(self.options.device)
|
||||
if not info:
|
||||
sys.stdout.write('Drive caching behaviour not saved: could not get device info (requires pycdio).\n')
|
||||
sys.stdout.write('Drive caching behaviour not saved:'
|
||||
'could not get device info (requires pycdio).\n')
|
||||
return
|
||||
|
||||
sys.stdout.write(
|
||||
'Adding drive cache behaviour to configuration file.\n')
|
||||
|
||||
config.Config().setDefeatsCache(info[0], info[1], info[2],
|
||||
t.defeatsCache)
|
||||
config.Config().setDefeatsCache(
|
||||
info[0], info[1], info[2], t.defeatsCache)
|
||||
|
||||
|
||||
class List(BaseCommand):
|
||||
@@ -77,7 +79,7 @@ class List(BaseCommand):
|
||||
return
|
||||
|
||||
try:
|
||||
import cdio as _
|
||||
import cdio as _ # noqa: F401 (TODO: fix it in a separate PR?)
|
||||
except ImportError:
|
||||
sys.stdout.write(
|
||||
'Install pycdio for vendor/model/release detection.\n')
|
||||
@@ -87,7 +89,7 @@ class List(BaseCommand):
|
||||
vendor, model, release = drive.getDeviceInfo(path)
|
||||
sys.stdout.write(
|
||||
"drive: %s, vendor: %s, model: %s, release: %s\n" % (
|
||||
path, vendor, model, release))
|
||||
path, vendor, model, release))
|
||||
|
||||
try:
|
||||
offset = self.config.getReadOffset(
|
||||
@@ -95,8 +97,10 @@ class List(BaseCommand):
|
||||
sys.stdout.write(
|
||||
" Configured read offset: %d\n" % offset)
|
||||
except KeyError:
|
||||
sys.stdout.write(
|
||||
" No read offset found. Run 'whipper offset find'\n")
|
||||
# Note spaces at the beginning for pretty terminal output
|
||||
sys.stdout.write(" "
|
||||
"No read offset found. "
|
||||
"Run 'whipper offset find'\n")
|
||||
|
||||
try:
|
||||
defeats = self.config.getDefeatsCache(
|
||||
@@ -108,7 +112,6 @@ class List(BaseCommand):
|
||||
" Unknown whether audio cache can be defeated. "
|
||||
"Run 'whipper drive analyze'\n")
|
||||
|
||||
|
||||
if not paths:
|
||||
sys.stdout.write('No drives found.\n')
|
||||
|
||||
|
||||
@@ -72,11 +72,11 @@ Retags the image from the given .cue files with tags obtained from MusicBrainz.
|
||||
sys.stdout.write('MusicBrainz disc id is %s\n' % mbdiscid)
|
||||
|
||||
sys.stdout.write("MusicBrainz lookup URL %s\n" %
|
||||
cueImage.table.getMusicBrainzSubmitURL())
|
||||
cueImage.table.getMusicBrainzSubmitURL())
|
||||
prog.metadata = prog.getMusicBrainz(cueImage.table, mbdiscid,
|
||||
release=self.options.release_id,
|
||||
country=self.options.country,
|
||||
prompt=self.options.prompt)
|
||||
release=self.options.release_id, # noqa: E501
|
||||
country=self.options.country,
|
||||
prompt=self.options.prompt)
|
||||
|
||||
if not prog.metadata:
|
||||
print 'Not in MusicBrainz database, skipping'
|
||||
|
||||
@@ -17,6 +17,7 @@ from whipper.program.utils import eject_device
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def main():
|
||||
# set user agent
|
||||
musicbrainzngs.set_useragent("whipper", whipper.__version__,
|
||||
@@ -47,7 +48,7 @@ def main():
|
||||
|
||||
if isinstance(e.exception, common.EmptyError):
|
||||
logger.debug("EmptyError: %r", str(e.exception))
|
||||
sys.stderr.write('whipper: error: Could not create encoded file.\n')
|
||||
sys.stderr.write('whipper: error: Could not create encoded file.\n') # noqa: E501
|
||||
return 255
|
||||
|
||||
# in python3 we can instead do `raise e.exception` as that would show
|
||||
@@ -56,6 +57,7 @@ def main():
|
||||
return 255
|
||||
return ret if ret else 0
|
||||
|
||||
|
||||
class Whipper(BaseCommand):
|
||||
description = """whipper is a CD ripping utility focusing on accuracy over speed.
|
||||
|
||||
@@ -74,18 +76,20 @@ You can get help on subcommands by using the -h option to the subcommand.
|
||||
|
||||
def add_arguments(self):
|
||||
self.parser.add_argument('-R', '--record',
|
||||
action='store_true', dest='record',
|
||||
help="record API requests for playback")
|
||||
action='store_true', dest='record',
|
||||
help="record API requests for playback")
|
||||
self.parser.add_argument('-v', '--version',
|
||||
action="store_true", dest="version",
|
||||
help="show version information")
|
||||
action="store_true", dest="version",
|
||||
help="show version information")
|
||||
self.parser.add_argument('-h', '--help',
|
||||
action="store_true", dest="help",
|
||||
help="show this help message and exit")
|
||||
action="store_true", dest="help",
|
||||
help="show this help message and exit")
|
||||
self.parser.add_argument('-e', '--eject',
|
||||
action="store", dest="eject", default="always",
|
||||
choices=('never', 'failure', 'success', 'always'),
|
||||
help="when to eject disc (default: always)")
|
||||
action="store", dest="eject",
|
||||
default="always",
|
||||
choices=('never', 'failure',
|
||||
'success', 'always'),
|
||||
help="when to eject disc (default: always)")
|
||||
|
||||
def handle_arguments(self):
|
||||
if self.options.help:
|
||||
|
||||
@@ -22,19 +22,17 @@ import argparse
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import logging
|
||||
import gobject
|
||||
gobject.threads_init()
|
||||
|
||||
from whipper.command.basecommand import BaseCommand
|
||||
from whipper.common import accurip, common, config, drive, program
|
||||
from whipper.common import accurip, common, config, drive
|
||||
from whipper.common import task as ctask
|
||||
from whipper.program import cdrdao, cdparanoia, utils
|
||||
from whipper.common import checksum
|
||||
|
||||
from whipper.extern.task import task
|
||||
|
||||
import logging
|
||||
gobject.threads_init()
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# see http://www.accuraterip.com/driveoffsets.htm
|
||||
@@ -117,7 +115,7 @@ CD in the AccurateRip database."""
|
||||
|
||||
if responses[0].cddbDiscId != table.getCDDBDiscId():
|
||||
logger.warning("AccurateRip response discid different: %s",
|
||||
responses[0].cddbDiscId)
|
||||
responses[0].cddbDiscId)
|
||||
|
||||
# now rip the first track at various offsets, calculating AccurateRip
|
||||
# CRC, and matching it against the retrieved ones
|
||||
@@ -137,7 +135,7 @@ CD in the AccurateRip database."""
|
||||
|
||||
# let MissingDependency fall through
|
||||
if isinstance(e.exception,
|
||||
common.MissingDependencyException):
|
||||
common.MissingDependencyException):
|
||||
raise e
|
||||
|
||||
if isinstance(e.exception, cdparanoia.FileSizeError):
|
||||
@@ -159,7 +157,7 @@ CD in the AccurateRip database."""
|
||||
logger.debug('MATCHED against response %d' % i)
|
||||
sys.stdout.write(
|
||||
'Offset of device is likely %d, confirming ...\n' %
|
||||
offset)
|
||||
offset)
|
||||
|
||||
# now try and rip all other tracks as well, except for the
|
||||
# last one (to avoid readers that can't do overread
|
||||
@@ -185,7 +183,7 @@ CD in the AccurateRip database."""
|
||||
else:
|
||||
sys.stdout.write(
|
||||
'Only %d of %d tracks matched, continuing ...\n' % (
|
||||
count, len(table.tracks)))
|
||||
count, len(table.tracks)))
|
||||
|
||||
sys.stdout.write('No matching offset found.\n')
|
||||
sys.stdout.write('Consider trying again with a different disc.\n')
|
||||
@@ -201,16 +199,19 @@ CD in the AccurateRip database."""
|
||||
os.close(fd)
|
||||
|
||||
t = cdparanoia.ReadTrackTask(path, table,
|
||||
table.getTrackStart(track), table.getTrackEnd(track),
|
||||
overread=False, offset=offset, device=self.options.device)
|
||||
table.getTrackStart(
|
||||
track), table.getTrackEnd(track),
|
||||
overread=False, offset=offset,
|
||||
device=self.options.device)
|
||||
t.description = 'Ripping track %d with read offset %d' % (
|
||||
track, offset)
|
||||
runner.run(t)
|
||||
|
||||
|
||||
# TODO MW: Update this to also use the v2 checksum(s)
|
||||
t = checksum.FastAccurateRipChecksumTask(path, trackNumber=track,
|
||||
trackCount=len(table.tracks), wave=True, v2=False)
|
||||
t = checksum.FastAccurateRipChecksumTask(path,
|
||||
trackNumber=track,
|
||||
trackCount=len(table.tracks),
|
||||
wave=True, v2=False)
|
||||
runner.run(t)
|
||||
|
||||
os.unlink(path)
|
||||
@@ -218,17 +219,19 @@ CD in the AccurateRip database."""
|
||||
|
||||
def _foundOffset(self, device, offset):
|
||||
sys.stdout.write('\nRead offset of device is: %d.\n' %
|
||||
offset)
|
||||
offset)
|
||||
|
||||
info = drive.getDeviceInfo(device)
|
||||
if not info:
|
||||
sys.stdout.write('Offset not saved: could not get device info (requires pycdio).\n')
|
||||
sys.stdout.write(
|
||||
'Offset not saved: could not get '
|
||||
'device info (requires pycdio).\n')
|
||||
return
|
||||
|
||||
sys.stdout.write('Adding read offset to configuration file.\n')
|
||||
|
||||
config.Config().setReadOffset(info[0], info[1], info[2],
|
||||
offset)
|
||||
offset)
|
||||
|
||||
|
||||
class Offset(BaseCommand):
|
||||
|
||||
@@ -30,6 +30,7 @@ from whipper.common import directory
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Persister:
|
||||
"""
|
||||
I wrap an optional pickle to persist an object to disk.
|
||||
@@ -125,7 +126,7 @@ class PersistedCache:
|
||||
try:
|
||||
os.makedirs(self.path)
|
||||
except OSError, e:
|
||||
if e.errno != 17: # FIXME
|
||||
if e.errno != 17: # FIXME
|
||||
raise
|
||||
|
||||
def _getPath(self, key):
|
||||
@@ -176,7 +177,7 @@ class ResultCache:
|
||||
presult.persist(presult.object)
|
||||
else:
|
||||
logger.debug('result for cddbdiscid %r found in cache, reusing',
|
||||
cddbdiscid)
|
||||
cddbdiscid)
|
||||
|
||||
return presult
|
||||
|
||||
@@ -218,7 +219,7 @@ class TableCache:
|
||||
ptable.object = None
|
||||
else:
|
||||
logger.debug('no valid cached table found for %r' %
|
||||
cddbdiscid)
|
||||
cddbdiscid)
|
||||
|
||||
if not ptable.object:
|
||||
# get an empty persistable from the writable location
|
||||
|
||||
@@ -67,8 +67,9 @@ class FastAccurateRipChecksumTask(etask.Task):
|
||||
self.schedule(0.0, self._arc)
|
||||
|
||||
def _arc(self):
|
||||
arc = accuraterip_checksum(self.path, self.trackNumber, self.trackCount,
|
||||
self._wave, self._v2)
|
||||
arc = accuraterip_checksum(self.path, self.trackNumber,
|
||||
self.trackCount,
|
||||
self._wave, self._v2)
|
||||
self.checksum = arc
|
||||
|
||||
self.stop()
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import commands
|
||||
import math
|
||||
import subprocess
|
||||
|
||||
@@ -32,7 +31,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
FRAMES_PER_SECOND = 75
|
||||
|
||||
SAMPLES_PER_FRAME = 588 # a sample is 2 16-bit values, left and right channel
|
||||
SAMPLES_PER_FRAME = 588 # a sample is 2 16-bit values, left and right channel
|
||||
WORDS_PER_FRAME = SAMPLES_PER_FRAME * 2
|
||||
BYTES_PER_FRAME = SAMPLES_PER_FRAME * 4
|
||||
|
||||
@@ -41,6 +40,7 @@ class EjectError(SystemError):
|
||||
"""
|
||||
Possibly ejects the drive in command.main.
|
||||
"""
|
||||
|
||||
def __init__(self, device, *args):
|
||||
"""
|
||||
args is a tuple used by BaseException.__str__
|
||||
@@ -60,7 +60,7 @@ def msfToFrames(msf):
|
||||
@rtype: int
|
||||
@returns: number of frames
|
||||
"""
|
||||
if not ':' in msf:
|
||||
if ':' not in msf:
|
||||
return int(msf)
|
||||
|
||||
m, s, f = msf.split(':')
|
||||
@@ -133,6 +133,7 @@ def formatTime(seconds, fractional=3):
|
||||
|
||||
return " ".join(chunks)
|
||||
|
||||
|
||||
class MissingDependencyException(Exception):
|
||||
dependency = None
|
||||
|
||||
@@ -144,6 +145,7 @@ class MissingDependencyException(Exception):
|
||||
class EmptyError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class MissingFrames(Exception):
|
||||
"""
|
||||
Less frames decoded than expected.
|
||||
@@ -289,8 +291,8 @@ class VersionGetter(object):
|
||||
|
||||
try:
|
||||
p = asyncsub.Popen(self._args,
|
||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE, close_fds=True)
|
||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE, close_fds=True)
|
||||
p.wait()
|
||||
output = asyncsub.recv_some(p, e=0, stderr=1)
|
||||
vre = self._regexp.search(output)
|
||||
|
||||
@@ -30,6 +30,7 @@ from whipper.common import directory
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Config:
|
||||
|
||||
def __init__(self, path=None):
|
||||
@@ -46,7 +47,7 @@ class Config:
|
||||
self._parser.readfp(f)
|
||||
|
||||
logger.info('Loaded %d sections from config file' %
|
||||
len(self._parser.sections()))
|
||||
len(self._parser.sections()))
|
||||
|
||||
def write(self):
|
||||
fd, path = tempfile.mkstemp(suffix=u'.whipperrc')
|
||||
@@ -55,8 +56,7 @@ class Config:
|
||||
handle.close()
|
||||
shutil.move(path, self._path)
|
||||
|
||||
|
||||
### any section
|
||||
# any section
|
||||
|
||||
def _getter(self, suffix, section, option):
|
||||
methodName = 'get' + suffix
|
||||
@@ -72,7 +72,7 @@ class Config:
|
||||
def getboolean(self, section, option):
|
||||
return self._getter('boolean', section, option)
|
||||
|
||||
### drive sections
|
||||
# drive sections
|
||||
|
||||
def setReadOffset(self, vendor, model, release, offset):
|
||||
"""
|
||||
@@ -96,7 +96,6 @@ class Config:
|
||||
raise KeyError("Could not find read_offset for %s/%s/%s" % (
|
||||
vendor, model, release))
|
||||
|
||||
|
||||
def setDefeatsCache(self, vendor, model, release, defeat):
|
||||
"""
|
||||
Set whether the drive defeats the cache.
|
||||
@@ -139,7 +138,7 @@ class Config:
|
||||
return name
|
||||
|
||||
raise KeyError("Could not find configuration section for %s/%s/%s" % (
|
||||
vendor, model, release))
|
||||
vendor, model, release))
|
||||
|
||||
def _findOrCreateDriveSection(self, vendor, model, release):
|
||||
try:
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
from os import getenv, makedirs
|
||||
from os.path import join, expanduser, exists
|
||||
|
||||
|
||||
def config_path():
|
||||
path = join(getenv('XDG_CONFIG_HOME') or join(expanduser('~'), u'.config'),
|
||||
u'whipper')
|
||||
@@ -28,6 +29,7 @@ def config_path():
|
||||
makedirs(path)
|
||||
return join(path, u'whipper.conf')
|
||||
|
||||
|
||||
def cache_path(name=None):
|
||||
path = join(getenv('XDG_CACHE_HOME') or join(expanduser('~'), u'.cache'),
|
||||
u'whipper')
|
||||
@@ -37,9 +39,10 @@ def cache_path(name=None):
|
||||
makedirs(path)
|
||||
return path
|
||||
|
||||
|
||||
def data_path(name=None):
|
||||
path = join(getenv('XDG_DATA_HOME')
|
||||
or join(expanduser('~'), u'.local/share'),
|
||||
path = join(getenv('XDG_DATA_HOME') or
|
||||
join(expanduser('~'), u'.local/share'),
|
||||
u'whipper')
|
||||
if name:
|
||||
path = join(path, name)
|
||||
|
||||
@@ -23,6 +23,7 @@ import os
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _listify(listOrString):
|
||||
if type(listOrString) == str:
|
||||
return [listOrString, ]
|
||||
@@ -64,7 +65,6 @@ def getDeviceInfo(path):
|
||||
import cdio
|
||||
except ImportError:
|
||||
return None
|
||||
|
||||
device = cdio.Device(path)
|
||||
ok, vendor, model, release = device.get_hwinfo()
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ from whipper.program import flac
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SoxPeakTask(task.Task):
|
||||
description = 'Calculating peak level'
|
||||
|
||||
@@ -44,6 +45,7 @@ class SoxPeakTask(task.Task):
|
||||
self.peak = sox.peak_level(self.track_path)
|
||||
self.stop()
|
||||
|
||||
|
||||
class FlacEncodeTask(task.Task):
|
||||
description = 'Encoding to FLAC'
|
||||
|
||||
@@ -61,11 +63,13 @@ class FlacEncodeTask(task.Task):
|
||||
self.new_path = flac.encode(self.track_path, self.track_out_path)
|
||||
self.stop()
|
||||
|
||||
# TODO: Wizzup: Do we really want this as 'Task'...?
|
||||
# I only made it a task for now because that it's easier to integrate in
|
||||
# program/cdparanoia.py - where whipper currently does the tagging.
|
||||
# We should just move the tagging to a more sensible place.
|
||||
|
||||
class TaggingTask(task.Task):
|
||||
# TODO: Wizzup: Do we really want this as 'Task'...?
|
||||
# I only made it a task for now because that it's easier to integrate in
|
||||
# program/cdparanoia.py - where whipper currently does the tagging.
|
||||
# We should just move the tagging to a more sensible place.
|
||||
|
||||
description = 'Writing tags to FLAC'
|
||||
|
||||
def __init__(self, track_path, tags):
|
||||
|
||||
@@ -28,7 +28,7 @@ import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
VA_ID = "89ad4ac3-39f7-470e-963a-56509c546377" # Various Artists
|
||||
VA_ID = "89ad4ac3-39f7-470e-963a-56509c546377" # Various Artists
|
||||
|
||||
|
||||
class MusicBrainzException(Exception):
|
||||
@@ -47,7 +47,7 @@ class NotFoundException(MusicBrainzException):
|
||||
class TrackMetadata(object):
|
||||
artist = None
|
||||
title = None
|
||||
duration = None # in ms
|
||||
duration = None # in ms
|
||||
mbid = None
|
||||
sortName = None
|
||||
mbidArtist = None
|
||||
@@ -131,7 +131,6 @@ class _Credit(list):
|
||||
|
||||
return "".join(res)
|
||||
|
||||
|
||||
def getSortName(self):
|
||||
return self.joiner(lambda i: i.get('artist').get('sort-name', None))
|
||||
|
||||
@@ -140,7 +139,7 @@ class _Credit(list):
|
||||
|
||||
def getIds(self):
|
||||
return self.joiner(lambda i: i.get('artist').get('id', None),
|
||||
joinString=";")
|
||||
joinString=";")
|
||||
|
||||
|
||||
def _getMetadata(releaseShort, release, discid, country=None):
|
||||
@@ -152,7 +151,7 @@ def _getMetadata(releaseShort, release, discid, country=None):
|
||||
@rtype: L{DiscMetadata} or None
|
||||
"""
|
||||
logger.debug('getMetadata for release id %r',
|
||||
release['id'])
|
||||
release['id'])
|
||||
if not release['id']:
|
||||
logger.warning('No id for release %r', release)
|
||||
return None
|
||||
@@ -173,7 +172,6 @@ def _getMetadata(releaseShort, release, discid, country=None):
|
||||
if discCredit[0]['artist']['id'] == VA_ID:
|
||||
discMD.various = True
|
||||
|
||||
|
||||
if len(discCredit) > 1:
|
||||
logger.debug('artist-credit more than 1: %r', discCredit)
|
||||
|
||||
@@ -233,8 +231,9 @@ def _getMetadata(releaseShort, release, discid, country=None):
|
||||
# FIXME: unit of duration ?
|
||||
track.duration = int(t['recording'].get('length', 0))
|
||||
if not track.duration:
|
||||
logger.warning('track %r (%r) does not have duration' % (
|
||||
track.title, track.mbid))
|
||||
logger.warning(
|
||||
'track %r (%r) does not have duration' %
|
||||
(track.title, track.mbid))
|
||||
tainted = True
|
||||
else:
|
||||
duration += track.duration
|
||||
@@ -270,8 +269,8 @@ def musicbrainz(discid, country=None, record=False):
|
||||
ret = []
|
||||
|
||||
try:
|
||||
result = musicbrainzngs.get_releases_by_discid(discid,
|
||||
includes=["artists", "recordings", "release-groups"])
|
||||
result = musicbrainzngs.get_releases_by_discid(
|
||||
discid, includes=["artists", "recordings", "release-groups"])
|
||||
except musicbrainzngs.ResponseError, e:
|
||||
if isinstance(e.cause, urllib2.HTTPError):
|
||||
if e.cause.code == 404:
|
||||
@@ -284,7 +283,7 @@ def musicbrainz(discid, country=None, record=False):
|
||||
# The result can either be a "disc" or a "cdstub"
|
||||
if result.get('disc'):
|
||||
logger.debug('found %d releases for discid %r',
|
||||
len(result['disc']['release-list']), discid)
|
||||
len(result['disc']['release-list']), discid)
|
||||
_record(record, 'releases', discid, result)
|
||||
|
||||
# Display the returned results to the user.
|
||||
|
||||
@@ -51,12 +51,13 @@ class PathFilter(object):
|
||||
# change all fancy single/double quotes to normal quotes
|
||||
if self._quotes:
|
||||
path = re.sub(ur'[\xc2\xb4\u2018\u2019\u201b]', "'", path,
|
||||
re.UNICODE)
|
||||
re.UNICODE)
|
||||
path = re.sub(ur'[\u201c\u201d\u201f]', '"', path, re.UNICODE)
|
||||
|
||||
if self._special:
|
||||
path = separators(path)
|
||||
path = re.sub(r'[\*\?&!\'\"\$\(\)`{}\[\]<>]', '_', path, re.UNICODE)
|
||||
path = re.sub(r'[\*\?&!\'\"\$\(\)`{}\[\]<>]',
|
||||
'_', path, re.UNICODE)
|
||||
|
||||
if self._fat:
|
||||
path = separators(path)
|
||||
|
||||
@@ -76,7 +76,7 @@ class Program:
|
||||
'special': False
|
||||
}.items():
|
||||
value = None
|
||||
value = self._config.getboolean('main', 'path_filter_'+ key)
|
||||
value = self._config.getboolean('main', 'path_filter_' + key)
|
||||
if value is None:
|
||||
value = default
|
||||
|
||||
@@ -105,9 +105,8 @@ class Program:
|
||||
version = cdrdao.getCDRDAOVersion()
|
||||
if V(version) < V('1.2.3rc2'):
|
||||
sys.stdout.write('Warning: cdrdao older than 1.2.3 has a '
|
||||
'pre-gap length bug.\n'
|
||||
'See http://sourceforge.net/tracker/?func=detail'
|
||||
'&aid=604751&group_id=2171&atid=102171\n')
|
||||
'pre-gap length bug.\n'
|
||||
'See http://sourceforge.net/tracker/?func=detail&aid=604751&group_id=2171&atid=102171\n') # noqa: E501
|
||||
t = cdrdao.ReadTOCTask(device)
|
||||
ptoc.persist(t.table)
|
||||
toc = ptoc.object
|
||||
@@ -133,17 +132,17 @@ class Program:
|
||||
itable = tdict[offset]
|
||||
|
||||
if not itable:
|
||||
logger.debug('getTable: cddbdiscid %s, mbdiscid %s not in cache for offset %s, '
|
||||
'reading table' % (
|
||||
cddbdiscid, mbdiscid, offset))
|
||||
logger.debug('getTable: cddbdiscid %s, mbdiscid %s not '
|
||||
'in cache for offset %s, reading table' % (
|
||||
cddbdiscid, mbdiscid, offset))
|
||||
t = cdrdao.ReadTableTask(device)
|
||||
itable = t.table
|
||||
tdict[offset] = itable
|
||||
ptable.persist(tdict)
|
||||
logger.debug('getTable: read table %r' % itable)
|
||||
else:
|
||||
logger.debug('getTable: cddbdiscid %s, mbdiscid %s in cache for offset %s' % (
|
||||
cddbdiscid, mbdiscid, offset))
|
||||
logger.debug('getTable: cddbdiscid %s, mbdiscid %s in cache '
|
||||
'for offset %s' % (cddbdiscid, mbdiscid, offset))
|
||||
logger.debug('getTable: loaded table %r' % itable)
|
||||
|
||||
assert itable.hasTOC()
|
||||
@@ -151,7 +150,7 @@ class Program:
|
||||
self.result.table = itable
|
||||
|
||||
logger.debug('getTable: returning table with mb id %s' %
|
||||
itable.getMusicBrainzDiscId())
|
||||
itable.getMusicBrainzDiscId())
|
||||
return itable
|
||||
|
||||
def getRipResult(self, cddbdiscid):
|
||||
@@ -200,11 +199,11 @@ class Program:
|
||||
|
||||
# default values
|
||||
v['A'] = 'Unknown Artist'
|
||||
v['d'] = mbdiscid # fallback for title
|
||||
v['d'] = mbdiscid # fallback for title
|
||||
v['r'] = 'unknown'
|
||||
v['R'] = 'Unknown'
|
||||
v['B'] = '' # barcode
|
||||
v['C'] = '' # catalog number
|
||||
v['B'] = '' # barcode
|
||||
v['C'] = '' # catalog number
|
||||
v['x'] = 'flac'
|
||||
v['X'] = v['x'].upper()
|
||||
v['y'] = '0000'
|
||||
@@ -215,7 +214,6 @@ class Program:
|
||||
else:
|
||||
v['n'] = 'Unknown Track %d' % i
|
||||
|
||||
|
||||
if self.metadata:
|
||||
release = self.metadata.release or '0000'
|
||||
v['y'] = release[:4]
|
||||
@@ -229,10 +227,12 @@ class Program:
|
||||
v['r'] = self.metadata.releaseType.lower()
|
||||
if i > 0:
|
||||
try:
|
||||
v['a'] = self._filter.filter(self.metadata.tracks[i - 1].artist)
|
||||
v['a'] = self._filter.filter(
|
||||
self.metadata.tracks[i - 1].artist)
|
||||
v['s'] = self._filter.filter(
|
||||
self.metadata.tracks[i - 1].sortName)
|
||||
v['n'] = self._filter.filter(self.metadata.tracks[i - 1].title)
|
||||
v['n'] = self._filter.filter(
|
||||
self.metadata.tracks[i - 1].title)
|
||||
except IndexError, e:
|
||||
print 'ERROR: no track %d found, %r' % (i, e)
|
||||
raise
|
||||
@@ -255,8 +255,6 @@ class Program:
|
||||
|
||||
ret = os.path.join(outdir, template % v)
|
||||
|
||||
|
||||
|
||||
return ret
|
||||
|
||||
def getCDDB(self, cddbdiscid):
|
||||
@@ -282,7 +280,8 @@ class Program:
|
||||
|
||||
return None
|
||||
|
||||
def getMusicBrainz(self, ittoc, mbdiscid, release=None, country=None, prompt=False):
|
||||
def getMusicBrainz(self, ittoc, mbdiscid, release=None, country=None,
|
||||
prompt=False):
|
||||
"""
|
||||
@type ittoc: L{whipper.image.table.Table}
|
||||
"""
|
||||
@@ -291,7 +290,7 @@ class Program:
|
||||
common.formatTime(ittoc.duration() / 1000.0),
|
||||
ittoc.getAudioTracks()))
|
||||
logger.debug('MusicBrainz submit url: %r',
|
||||
ittoc.getMusicBrainzSubmitURL())
|
||||
ittoc.getMusicBrainzSubmitURL())
|
||||
ret = None
|
||||
|
||||
metadatas = None
|
||||
@@ -300,8 +299,8 @@ class Program:
|
||||
for _ in range(0, 4):
|
||||
try:
|
||||
metadatas = mbngs.musicbrainz(mbdiscid,
|
||||
country=country,
|
||||
record=self._record)
|
||||
country=country,
|
||||
record=self._record)
|
||||
break
|
||||
except mbngs.NotFoundException, e:
|
||||
break
|
||||
@@ -326,21 +325,23 @@ class Program:
|
||||
for metadata in metadatas:
|
||||
self._stdout.write('\n')
|
||||
self._stdout.write('Artist : %s\n' %
|
||||
metadata.artist.encode('utf-8'))
|
||||
metadata.artist.encode('utf-8'))
|
||||
self._stdout.write('Title : %s\n' %
|
||||
metadata.title.encode('utf-8'))
|
||||
metadata.title.encode('utf-8'))
|
||||
self._stdout.write('Duration: %s\n' %
|
||||
common.formatTime(metadata.duration / 1000.0))
|
||||
common.formatTime(metadata.duration /
|
||||
1000.0))
|
||||
self._stdout.write('URL : %s\n' % metadata.url)
|
||||
self._stdout.write('Release : %s\n' % metadata.mbid)
|
||||
self._stdout.write('Type : %s\n' % metadata.releaseType)
|
||||
if metadata.barcode:
|
||||
self._stdout.write("Barcode : %s\n" % metadata.barcode)
|
||||
if metadata.catalogNumber:
|
||||
self._stdout.write("Cat no : %s\n" % metadata.catalogNumber)
|
||||
self._stdout.write("Cat no : %s\n" %
|
||||
metadata.catalogNumber)
|
||||
|
||||
delta = abs(metadata.duration - ittoc.duration())
|
||||
if not delta in deltas:
|
||||
if delta not in deltas:
|
||||
deltas[delta] = []
|
||||
deltas[delta].append(metadata)
|
||||
|
||||
@@ -352,7 +353,8 @@ class Program:
|
||||
|
||||
if prompt:
|
||||
guess = (deltas[lowest])[0].mbid
|
||||
release = raw_input("\nPlease select a release [%s]: " % guess)
|
||||
release = raw_input(
|
||||
"\nPlease select a release [%s]: " % guess)
|
||||
|
||||
if not release:
|
||||
release = guess
|
||||
@@ -360,15 +362,15 @@ class Program:
|
||||
if release:
|
||||
metadatas = [m for m in metadatas if m.url.endswith(release)]
|
||||
logger.debug('Asked for release %r, only kept %r',
|
||||
release, metadatas)
|
||||
release, metadatas)
|
||||
if len(metadatas) == 1:
|
||||
self._stdout.write('\n')
|
||||
self._stdout.write('Picked requested release id %s\n' %
|
||||
release)
|
||||
release)
|
||||
self._stdout.write('Artist : %s\n' %
|
||||
metadatas[0].artist.encode('utf-8'))
|
||||
metadatas[0].artist.encode('utf-8'))
|
||||
self._stdout.write('Title : %s\n' %
|
||||
metadatas[0].title.encode('utf-8'))
|
||||
metadatas[0].title.encode('utf-8'))
|
||||
elif not metadatas:
|
||||
self._stdout.write(
|
||||
"Requested release id '%s', "
|
||||
@@ -385,22 +387,23 @@ class Program:
|
||||
for i, metadata in enumerate(metadatas):
|
||||
if not artist == metadata.artist:
|
||||
logger.warning("artist 0: %r and artist %d: %r "
|
||||
"are not the same" % (
|
||||
artist, i, metadata.artist))
|
||||
"are not the same" % (
|
||||
artist, i, metadata.artist))
|
||||
if not releaseTitle == metadata.releaseTitle:
|
||||
logger.warning("title 0: %r and title %d: %r "
|
||||
"are not the same" % (
|
||||
releaseTitle, i, metadata.releaseTitle))
|
||||
"are not the same" % (
|
||||
releaseTitle, i,
|
||||
metadata.releaseTitle))
|
||||
|
||||
if (not release and len(deltas.keys()) > 1):
|
||||
self._stdout.write('\n')
|
||||
self._stdout.write('Picked closest match in duration.\n')
|
||||
self._stdout.write('Others may be wrong in MusicBrainz, '
|
||||
'please correct.\n')
|
||||
'please correct.\n')
|
||||
self._stdout.write('Artist : %s\n' %
|
||||
artist.encode('utf-8'))
|
||||
artist.encode('utf-8'))
|
||||
self._stdout.write('Title : %s\n' %
|
||||
metadatas[0].title.encode('utf-8'))
|
||||
metadatas[0].title.encode('utf-8'))
|
||||
|
||||
# Select one of the returned releases. We just pick the first one.
|
||||
ret = metadatas[0]
|
||||
@@ -503,12 +506,13 @@ class Program:
|
||||
raise
|
||||
|
||||
ret = trackResult.testcrc == t.checksum
|
||||
logger.debug('verifyTrack: track result crc %r, file crc %r, result %r',
|
||||
logger.debug('verifyTrack: track result crc %r, '
|
||||
'file crc %r, result %r',
|
||||
trackResult.testcrc, t.checksum, ret)
|
||||
return ret
|
||||
|
||||
def ripTrack(self, runner, trackResult, offset, device, taglist,
|
||||
overread, what=None):
|
||||
overread, what=None):
|
||||
"""
|
||||
Ripping the track may change the track's filename as stored in
|
||||
trackResult.
|
||||
@@ -527,14 +531,15 @@ class Program:
|
||||
os.makedirs(dirname)
|
||||
|
||||
if not what:
|
||||
what='track %d' % (trackResult.number, )
|
||||
what = 'track %d' % (trackResult.number, )
|
||||
|
||||
t = cdparanoia.ReadVerifyTrackTask(trackResult.filename,
|
||||
self.result.table, start, stop, overread,
|
||||
offset=offset,
|
||||
device=device,
|
||||
taglist=taglist,
|
||||
what=what)
|
||||
self.result.table, start,
|
||||
stop, overread,
|
||||
offset=offset,
|
||||
device=device,
|
||||
taglist=taglist,
|
||||
what=what)
|
||||
|
||||
runner.run(t)
|
||||
|
||||
@@ -571,7 +576,7 @@ class Program:
|
||||
"""
|
||||
|
||||
logger.debug('verifying Image against %d AccurateRip responses',
|
||||
len(responses or []))
|
||||
len(responses or []))
|
||||
|
||||
cueImage = image.Image(self.cuePath)
|
||||
verifytask = image.ImageVerifyTask(cueImage)
|
||||
@@ -587,7 +592,6 @@ class Program:
|
||||
trackResult = self.result.getTrackResult(i + 1)
|
||||
trackResult.ARCRC = csum
|
||||
|
||||
|
||||
if not responses:
|
||||
logger.warning('No AccurateRip responses, cannot verify.')
|
||||
return
|
||||
@@ -615,8 +619,8 @@ class Program:
|
||||
trackResult.ARDBConfidence = confidence
|
||||
|
||||
if not trackResult.accurip:
|
||||
logger.warning("Track %02d: not matched in AccurateRip database",
|
||||
i + 1)
|
||||
logger.warning("Track %02d: not matched in "
|
||||
"AccurateRip database", i + 1)
|
||||
|
||||
# I have seen AccurateRip responses with 0 as confidence
|
||||
# for example, Best of Luke Haines, disc 1, track 1
|
||||
@@ -632,7 +636,7 @@ class Program:
|
||||
trackResult.ARDBMaxConfidence = maxConfidence
|
||||
if not response:
|
||||
logger.warning('Track %02d: none of the responses matched.',
|
||||
i + 1)
|
||||
i + 1)
|
||||
trackResult.ARDBCRC = int(
|
||||
maxResponse.checksums[i], 16)
|
||||
else:
|
||||
@@ -650,7 +654,7 @@ class Program:
|
||||
status = 'rip NOT accurate'
|
||||
|
||||
if trackResult.accurip:
|
||||
status = 'rip accurate '
|
||||
status = 'rip accurate '
|
||||
|
||||
c = "(not found) "
|
||||
ar = ", DB [notfound]"
|
||||
@@ -668,7 +672,7 @@ class Program:
|
||||
if trackResult.ARCRC is None:
|
||||
assert trackResult.number == 0, \
|
||||
'no trackResult.ARCRC on non-HTOA track %d' % \
|
||||
trackResult.number
|
||||
trackResult.number
|
||||
res.append("Track 0: unknown (not tracked)")
|
||||
else:
|
||||
res.append("Track %2d: %s %s [%08x]%s" % (
|
||||
|
||||
@@ -56,7 +56,6 @@ class Operator(object):
|
||||
operation = cls.deserialize(data)
|
||||
self._todo.append(operation)
|
||||
|
||||
|
||||
done = os.path.join(self._statePath, self._key + '.done')
|
||||
if os.path.exists(done):
|
||||
with open(done, 'r') as handle:
|
||||
|
||||
@@ -39,9 +39,11 @@ class PopenTask(task.Task):
|
||||
|
||||
try:
|
||||
self._popen = asyncsub.Popen(self.command,
|
||||
bufsize=self.bufsize,
|
||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE, close_fds=True, cwd=self.cwd)
|
||||
bufsize=self.bufsize,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
close_fds=True, cwd=self.cwd)
|
||||
except OSError, e:
|
||||
import errno
|
||||
if e.errno == errno.ENOENT:
|
||||
@@ -50,7 +52,7 @@ class PopenTask(task.Task):
|
||||
raise
|
||||
|
||||
logger.debug('Started %r with pid %d', self.command,
|
||||
self._popen.pid)
|
||||
self._popen.pid)
|
||||
|
||||
self.schedule(1.0, self._read, runner)
|
||||
|
||||
@@ -92,23 +94,23 @@ class PopenTask(task.Task):
|
||||
self.stop()
|
||||
|
||||
def _done(self):
|
||||
assert self._popen.returncode is not None, "No returncode"
|
||||
assert self._popen.returncode is not None, "No returncode"
|
||||
|
||||
if self._popen.returncode >= 0:
|
||||
logger.debug('Return code was %d', self._popen.returncode)
|
||||
else:
|
||||
logger.debug('Terminated with signal %d',
|
||||
-self._popen.returncode)
|
||||
if self._popen.returncode >= 0:
|
||||
logger.debug('Return code was %d', self._popen.returncode)
|
||||
else:
|
||||
logger.debug('Terminated with signal %d',
|
||||
-self._popen.returncode)
|
||||
|
||||
self.setProgress(1.0)
|
||||
self.setProgress(1.0)
|
||||
|
||||
if self._popen.returncode != 0:
|
||||
self.failed()
|
||||
else:
|
||||
self.done()
|
||||
if self._popen.returncode != 0:
|
||||
self.failed()
|
||||
else:
|
||||
self.done()
|
||||
|
||||
self.stop()
|
||||
return
|
||||
self.stop()
|
||||
return
|
||||
|
||||
def abort(self):
|
||||
logger.debug('Aborting, sending SIGTERM to %d', self._popen.pid)
|
||||
@@ -139,7 +141,6 @@ class PopenTask(task.Task):
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def commandMissing(self):
|
||||
"""
|
||||
Called when the command is missing.
|
||||
|
||||
10
whipper/extern/asyncsub.py
vendored
10
whipper/extern/asyncsub.py
vendored
@@ -95,7 +95,7 @@ class Popen(subprocess.Popen):
|
||||
try:
|
||||
written = os.write(self.stdin.fileno(), input)
|
||||
except OSError, why:
|
||||
if why[0] == errno.EPIPE: #broken pipe
|
||||
if why[0] == errno.EPIPE: # broken pipe
|
||||
return self._close('stdin')
|
||||
raise
|
||||
|
||||
@@ -108,7 +108,7 @@ class Popen(subprocess.Popen):
|
||||
|
||||
flags = fcntl.fcntl(conn, fcntl.F_GETFL)
|
||||
if not conn.closed:
|
||||
fcntl.fcntl(conn, fcntl.F_SETFL, flags| os.O_NONBLOCK)
|
||||
fcntl.fcntl(conn, fcntl.F_SETFL, flags | os.O_NONBLOCK)
|
||||
|
||||
try:
|
||||
if not select.select([conn], [], [], 0)[0]:
|
||||
@@ -125,13 +125,14 @@ class Popen(subprocess.Popen):
|
||||
if not conn.closed:
|
||||
fcntl.fcntl(conn, fcntl.F_SETFL, flags)
|
||||
|
||||
|
||||
message = "Other end disconnected!"
|
||||
|
||||
|
||||
def recv_some(p, t=.1, e=1, tr=5, stderr=0):
|
||||
if tr < 1:
|
||||
tr = 1
|
||||
x = time.time()+t
|
||||
x = time.time() + t
|
||||
y = []
|
||||
r = ''
|
||||
pr = p.recv
|
||||
@@ -147,7 +148,7 @@ def recv_some(p, t=.1, e=1, tr=5, stderr=0):
|
||||
elif r:
|
||||
y.append(r)
|
||||
else:
|
||||
time.sleep(max((x-time.time())/tr, 0))
|
||||
time.sleep(max((x - time.time()) / tr, 0))
|
||||
return ''.join(y)
|
||||
|
||||
|
||||
@@ -158,6 +159,7 @@ def send_all(p, data):
|
||||
raise Exception(message)
|
||||
data = buffer(data, sent)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if sys.platform == 'win32':
|
||||
shell, commands, tail = ('cmd', ('dir /w', 'echo HELLO WORLD'), '\r\n')
|
||||
|
||||
65
whipper/extern/task/task.py
vendored
65
whipper/extern/task/task.py
vendored
@@ -22,12 +22,13 @@ import sys
|
||||
|
||||
import gobject
|
||||
|
||||
|
||||
class TaskException(Exception):
|
||||
"""
|
||||
I wrap an exception that happened during task execution.
|
||||
"""
|
||||
|
||||
exception = None # original exception
|
||||
exception = None # original exception
|
||||
|
||||
def __init__(self, exception, message=None):
|
||||
self.exception = exception
|
||||
@@ -35,6 +36,8 @@ class TaskException(Exception):
|
||||
self.args = (exception, message, )
|
||||
|
||||
# lifted from flumotion log module
|
||||
|
||||
|
||||
def _getExceptionMessage(exception, frame=-1, filename=None):
|
||||
"""
|
||||
Return a short message based on an exception, useful for debugging.
|
||||
@@ -67,7 +70,7 @@ class LogStub(object):
|
||||
I am a stub for a log interface.
|
||||
"""
|
||||
|
||||
### log stubs
|
||||
# log stubs
|
||||
def log(self, message, *args):
|
||||
pass
|
||||
|
||||
@@ -112,8 +115,7 @@ class Task(LogStub):
|
||||
|
||||
_listeners = None
|
||||
|
||||
|
||||
### subclass methods
|
||||
# subclass methods
|
||||
def start(self, runner):
|
||||
"""
|
||||
Start the task.
|
||||
@@ -149,18 +151,20 @@ class Task(LogStub):
|
||||
self.running = False
|
||||
if not self.runner:
|
||||
print 'ERROR: stopping task which is already stopped'
|
||||
import traceback; traceback.print_stack()
|
||||
import traceback
|
||||
traceback.print_stack()
|
||||
self.runner = None
|
||||
self.debug('reset runner to None')
|
||||
self._notifyListeners('stopped')
|
||||
|
||||
### base class methods
|
||||
# base class methods
|
||||
def setProgress(self, value):
|
||||
"""
|
||||
Notify about progress changes bigger than the increment.
|
||||
Called by subclass implementations as the task progresses.
|
||||
"""
|
||||
if value - self.progress > self.increment or value >= 1.0 or value == 0.0:
|
||||
if (value - self.progress > self.increment or
|
||||
value >= 1.0 or value == 0.0):
|
||||
self.progress = value
|
||||
self._notifyListeners('progressed', value)
|
||||
self.log('notifying progress: %r on %r', value, self.description)
|
||||
@@ -186,8 +190,8 @@ class Task(LogStub):
|
||||
# for now
|
||||
if str(exception):
|
||||
msg = ": %s" % str(exception)
|
||||
line = "exception %(exc)s at %(filename)s:%(line)s: %(func)s()%(msg)s" \
|
||||
% locals()
|
||||
line = "exception %(exc)s at %(filename)s:%(line)s: "
|
||||
"%(func)s()%(msg)s" % locals()
|
||||
|
||||
self.exception = exception
|
||||
self.exceptionMessage = line
|
||||
@@ -211,11 +215,11 @@ class Task(LogStub):
|
||||
def schedule(self, delta, callable, *args, **kwargs):
|
||||
if not self.runner:
|
||||
print "ERROR: scheduling on a task that's altready stopped"
|
||||
import traceback; traceback.print_stack()
|
||||
import traceback
|
||||
traceback.print_stack()
|
||||
return
|
||||
self.runner.schedule(self, delta, callable, *args, **kwargs)
|
||||
|
||||
|
||||
def addListener(self, listener):
|
||||
"""
|
||||
Add a listener for task status changes.
|
||||
@@ -236,12 +240,14 @@ class Task(LogStub):
|
||||
except Exception, e:
|
||||
self.setException(e)
|
||||
|
||||
|
||||
# FIXME: should this become a real interface, like in zope ?
|
||||
class ITaskListener(object):
|
||||
"""
|
||||
I am an interface for objects listening to tasks.
|
||||
"""
|
||||
### listener callbacks
|
||||
# listener callbacks
|
||||
|
||||
def progressed(self, task, value):
|
||||
"""
|
||||
Implement me to be informed about progress.
|
||||
@@ -270,7 +276,6 @@ class ITaskListener(object):
|
||||
"""
|
||||
|
||||
|
||||
|
||||
# this is a Dummy task that can be used to test if this works at all
|
||||
class DummyTask(Task):
|
||||
def start(self, runner):
|
||||
@@ -286,6 +291,7 @@ class DummyTask(Task):
|
||||
|
||||
self.schedule(1.0, self._wind)
|
||||
|
||||
|
||||
class BaseMultiTask(Task, ITaskListener):
|
||||
"""
|
||||
I perform multiple tasks.
|
||||
@@ -336,20 +342,20 @@ class BaseMultiTask(Task, ITaskListener):
|
||||
task = self.tasks[self._task]
|
||||
self._task += 1
|
||||
self.debug('BaseMultiTask.next(): starting task %d of %d: %r',
|
||||
self._task, len(self.tasks), task)
|
||||
self._task, len(self.tasks), task)
|
||||
self.setDescription("%s (%d of %d) ..." % (
|
||||
task.description, self._task, len(self.tasks)))
|
||||
task.addListener(self)
|
||||
task.start(self.runner)
|
||||
self.debug('BaseMultiTask.next(): started task %d of %d: %r',
|
||||
self._task, len(self.tasks), task)
|
||||
self._task, len(self.tasks), task)
|
||||
except Exception, e:
|
||||
self.setException(e)
|
||||
self.debug('Got exception during next: %r', self.exceptionMessage)
|
||||
self.stop()
|
||||
return
|
||||
|
||||
### ITaskListener methods
|
||||
# ITaskListener methods
|
||||
def started(self, task):
|
||||
pass
|
||||
|
||||
@@ -362,10 +368,10 @@ class BaseMultiTask(Task, ITaskListener):
|
||||
They should fall through to chaining up if there is an exception.
|
||||
"""
|
||||
self.log('BaseMultiTask.stopped: task %r (%d of %d)',
|
||||
task, self.tasks.index(task) + 1, len(self.tasks))
|
||||
task, self.tasks.index(task) + 1, len(self.tasks))
|
||||
if task.exception:
|
||||
self.log('BaseMultiTask.stopped: exception %r',
|
||||
task.exceptionMessage)
|
||||
task.exceptionMessage)
|
||||
self.exception = task.exception
|
||||
self.exceptionMessage = task.exceptionMessage
|
||||
self.stop()
|
||||
@@ -395,10 +401,10 @@ class MultiSeparateTask(BaseMultiTask):
|
||||
def next(self):
|
||||
self.debug('MultiSeparateTask.next()')
|
||||
# start next task
|
||||
self.progress = 0.0 # reset progress for each task
|
||||
self.progress = 0.0 # reset progress for each task
|
||||
BaseMultiTask.next(self)
|
||||
|
||||
### ITaskListener methods
|
||||
# ITaskListener methods
|
||||
def progressed(self, task, value):
|
||||
self.setProgress(value)
|
||||
|
||||
@@ -406,6 +412,7 @@ class MultiSeparateTask(BaseMultiTask):
|
||||
self.setDescription("%s (%d of %d) ..." % (
|
||||
description, self._task, len(self.tasks)))
|
||||
|
||||
|
||||
class MultiCombinedTask(BaseMultiTask):
|
||||
"""
|
||||
I perform multiple tasks.
|
||||
@@ -415,7 +422,7 @@ class MultiCombinedTask(BaseMultiTask):
|
||||
description = 'Doing various tasks combined'
|
||||
_stopped = 0
|
||||
|
||||
### ITaskListener methods
|
||||
# ITaskListener methods
|
||||
def progressed(self, task, value):
|
||||
self.setProgress(float(self._stopped + value) / len(self.tasks))
|
||||
|
||||
@@ -424,6 +431,7 @@ class MultiCombinedTask(BaseMultiTask):
|
||||
self.setProgress(float(self._stopped) / len(self.tasks))
|
||||
BaseMultiTask.stopped(self, task)
|
||||
|
||||
|
||||
class TaskRunner(LogStub):
|
||||
"""
|
||||
I am a base class for task runners.
|
||||
@@ -439,7 +447,7 @@ class TaskRunner(LogStub):
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
### methods for tasks to call
|
||||
# methods for tasks to call
|
||||
def schedule(self, delta, callable, *args, **kwargs):
|
||||
"""
|
||||
Schedule a single future call.
|
||||
@@ -456,9 +464,10 @@ class SyncRunner(TaskRunner, ITaskListener):
|
||||
"""
|
||||
I run the task synchronously in a gobject MainLoop.
|
||||
"""
|
||||
|
||||
def __init__(self, verbose=True):
|
||||
self._verbose = verbose
|
||||
self._longest = 0 # longest string shown; for clearing
|
||||
self._longest = 0 # longest string shown; for clearing
|
||||
|
||||
def run(self, task, verbose=None, skip=False):
|
||||
self.debug('run task %r', task)
|
||||
@@ -500,26 +509,25 @@ class SyncRunner(TaskRunner, ITaskListener):
|
||||
self.debug('exception during start: %r', task.exceptionMessage)
|
||||
self.stopped(task)
|
||||
|
||||
|
||||
def schedule(self, task, delta, callable, *args, **kwargs):
|
||||
def c():
|
||||
try:
|
||||
self.log('schedule: calling %r(*args=%r, **kwargs=%r)',
|
||||
callable, args, kwargs)
|
||||
callable, args, kwargs)
|
||||
callable(*args, **kwargs)
|
||||
return False
|
||||
except Exception, e:
|
||||
self.debug('exception when calling scheduled callable %r',
|
||||
callable)
|
||||
callable)
|
||||
task.setException(e)
|
||||
self.stopped(task)
|
||||
raise
|
||||
self.log('schedule: scheduling %r(*args=%r, **kwargs=%r)',
|
||||
callable, args, kwargs)
|
||||
callable, args, kwargs)
|
||||
|
||||
gobject.timeout_add(int(delta * 1000L), c)
|
||||
|
||||
### ITaskListener methods
|
||||
# ITaskListener methods
|
||||
def progressed(self, task, value):
|
||||
if not self._verboseRun:
|
||||
return
|
||||
@@ -558,6 +566,7 @@ class SyncRunner(TaskRunner, ITaskListener):
|
||||
self._output('%s %3d %%' % (
|
||||
self._task.description, self._task.progress * 100.0))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
task = DummyTask()
|
||||
runner = SyncRunner()
|
||||
|
||||
@@ -142,11 +142,12 @@ class CueFile(object):
|
||||
+ minutes * common.FRAMES_PER_SECOND * 60
|
||||
|
||||
logger.debug('found index %d of track %r in %r:%d',
|
||||
indexNumber, currentTrack, currentFile.path, frameOffset)
|
||||
indexNumber, currentTrack, currentFile.path,
|
||||
frameOffset)
|
||||
# FIXME: what do we do about File's FORMAT ?
|
||||
currentTrack.index(indexNumber,
|
||||
path=currentFile.path, relative=frameOffset,
|
||||
counter=counter)
|
||||
path=currentFile.path, relative=frameOffset,
|
||||
counter=counter)
|
||||
continue
|
||||
|
||||
def message(self, number, message):
|
||||
@@ -166,8 +167,8 @@ class CueFile(object):
|
||||
# last track, so no length known
|
||||
return -1
|
||||
|
||||
thisIndex = track.indexes[1] # FIXME: could be more
|
||||
nextIndex = self.table.tracks[i + 1].indexes[1] # FIXME: could be 0
|
||||
thisIndex = track.indexes[1] # FIXME: could be more
|
||||
nextIndex = self.table.tracks[i + 1].indexes[1] # FIXME: could be 0
|
||||
|
||||
c = thisIndex.counter
|
||||
if c is not None and c == nextIndex.counter:
|
||||
|
||||
@@ -52,8 +52,8 @@ class Image(object):
|
||||
self._path = path
|
||||
self.cue = cue.CueFile(path)
|
||||
self.cue.parse()
|
||||
self._offsets = [] # 0 .. trackCount - 1
|
||||
self._lengths = [] # 0 .. trackCount - 1
|
||||
self._offsets = [] # 0 .. trackCount - 1
|
||||
self._lengths = [] # 0 .. trackCount - 1
|
||||
|
||||
self.table = None
|
||||
|
||||
@@ -98,8 +98,8 @@ class Image(object):
|
||||
# FIXME: this probably only works for non-compliant .CUE files
|
||||
# where pregap is put at end of previous file
|
||||
t.index(1, absolute=offset,
|
||||
path=self.cue.table.tracks[i].getIndex(1).path,
|
||||
relative=0)
|
||||
path=self.cue.table.tracks[i].getIndex(1).path,
|
||||
relative=0)
|
||||
|
||||
offset += length
|
||||
|
||||
@@ -128,17 +128,19 @@ class AccurateRipChecksumTask(task.MultiSeparateTask):
|
||||
index = track.indexes[1]
|
||||
length = cue.getTrackLength(track)
|
||||
if length < 0:
|
||||
logger.debug('track %d has unknown length' % (trackIndex + 1, ))
|
||||
logger.debug('track %d has unknown length' %
|
||||
(trackIndex + 1, ))
|
||||
else:
|
||||
logger.debug('track %d is %d samples long' % (
|
||||
trackIndex + 1, length))
|
||||
|
||||
path = image.getRealPath(index.path)
|
||||
|
||||
|
||||
checksumTask = checksum.FastAccurateRipChecksumTask(path,
|
||||
trackNumber=trackIndex + 1, trackCount=len(cue.table.tracks),
|
||||
wave=True, v2=False)
|
||||
checksumTask = checksum.FastAccurateRipChecksumTask(
|
||||
path,
|
||||
trackNumber=trackIndex + 1,
|
||||
trackCount=len(cue.table.tracks),
|
||||
wave=True, v2=False)
|
||||
|
||||
self.addTask(checksumTask)
|
||||
|
||||
@@ -201,8 +203,9 @@ class ImageVerifyTask(task.MultiSeparateTask):
|
||||
break
|
||||
|
||||
if taskk.length is None:
|
||||
raise ValueError("Track length was not found; look for "
|
||||
"earlier errors in debug log (set RIP_DEBUG=4)")
|
||||
raise ValueError("Track length was not found; "
|
||||
"look for earlier errors "
|
||||
"in debug log (set RIP_DEBUG=4)")
|
||||
index = track.indexes[1]
|
||||
assert taskk.length % common.SAMPLES_PER_FRAME == 0
|
||||
end = taskk.length / common.SAMPLES_PER_FRAME
|
||||
@@ -234,8 +237,9 @@ class ImageEncodeTask(task.MultiSeparateTask):
|
||||
root, ext = os.path.splitext(os.path.basename(path))
|
||||
outpath = os.path.join(outdir, root + '.' + 'flac')
|
||||
logger.debug('schedule encode to %r', outpath)
|
||||
taskk = encode.FlacEncodeTask(path, os.path.join(outdir,
|
||||
root + '.' + 'flac'))
|
||||
taskk = encode.FlacEncodeTask(path,
|
||||
os.path.join(outdir,
|
||||
root + '.' + 'flac'))
|
||||
self.addTask(taskk)
|
||||
|
||||
try:
|
||||
|
||||
@@ -169,9 +169,9 @@ class Table(object):
|
||||
@type cdtext: dict of str -> str
|
||||
"""
|
||||
|
||||
tracks = None # list of Track
|
||||
leadout = None # offset where the leadout starts
|
||||
catalog = None # catalog number; FIXME: is this UPC ?
|
||||
tracks = None # list of Track
|
||||
leadout = None # offset where the leadout starts
|
||||
catalog = None # catalog number; FIXME: is this UPC ?
|
||||
cdtext = None
|
||||
mbdiscid = None
|
||||
|
||||
@@ -305,7 +305,7 @@ class Table(object):
|
||||
# duration = durationFrames / common.FRAMES_PER_SECOND
|
||||
# assert t == duration, "%r != %r" % (t, duration)
|
||||
|
||||
debug.append(str(leadoutSeconds + 2)) # 2 is the 150 frame cddb offset
|
||||
debug.append(str(leadoutSeconds + 2)) # 2 is the 150 frame cddb offset
|
||||
result.append(leadoutSeconds)
|
||||
|
||||
value = (n % 0xff) << 24 | t << 8 | len(self.tracks)
|
||||
@@ -315,7 +315,7 @@ class Table(object):
|
||||
logger.debug('cddb values: %r', result)
|
||||
|
||||
logger.debug('cddb disc id debug: %s',
|
||||
" ".join(["%08x" % value, ] + debug))
|
||||
" ".join(["%08x" % value, ] + debug))
|
||||
|
||||
return result
|
||||
|
||||
@@ -472,7 +472,6 @@ class Table(object):
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
|
||||
logger.debug('MusicBrainz values: %r', result)
|
||||
return result
|
||||
|
||||
@@ -545,8 +544,8 @@ class Table(object):
|
||||
main = ['PERFORMER', 'TITLE']
|
||||
|
||||
for key in CDTEXT_FIELDS:
|
||||
if key not in main and key in self.cdtext:
|
||||
lines.append(" %s %s" % (key, self.cdtext[key]))
|
||||
if key not in main and key in self.cdtext:
|
||||
lines.append(" %s %s" % (key, self.cdtext[key]))
|
||||
|
||||
assert self.hasTOC(), "Table does not represent a full CD TOC"
|
||||
lines.append('REM DISCID %s' % self.getCDDBDiscId().upper())
|
||||
@@ -607,7 +606,7 @@ class Table(object):
|
||||
logger.debug('counter %d, writeFile' % counter)
|
||||
writeFile(index.path)
|
||||
logger.debug('setting counter to index.counter %r' %
|
||||
index.counter)
|
||||
index.counter)
|
||||
counter = index.counter
|
||||
|
||||
# any time we hit the first index, write a TRACK statement
|
||||
@@ -636,23 +635,25 @@ class Table(object):
|
||||
if not index00.path:
|
||||
length = indexOne.absolute - index00.absolute
|
||||
lines.append(" PREGAP %s" %
|
||||
common.framesToMSF(length))
|
||||
common.framesToMSF(length))
|
||||
continue
|
||||
|
||||
# handle any other INDEX 00 after its TRACK
|
||||
lines.append(" INDEX %02d %s" % (0,
|
||||
common.framesToMSF(index00.relative)))
|
||||
lines.append(" INDEX "
|
||||
"%02d %s" % (0, common.framesToMSF(
|
||||
index00.relative)))
|
||||
|
||||
if number > 0:
|
||||
# index 00 is output after TRACK up above
|
||||
lines.append(" INDEX %02d %s" % (number,
|
||||
common.framesToMSF(index.relative)))
|
||||
common.framesToMSF(
|
||||
index.relative)))
|
||||
|
||||
lines.append("")
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
### methods that modify the table
|
||||
# methods that modify the table
|
||||
|
||||
def clearFiles(self):
|
||||
"""
|
||||
@@ -689,13 +690,14 @@ class Table(object):
|
||||
@type index: C{int}
|
||||
"""
|
||||
logger.debug('setFile: track %d, index %d, path %r, '
|
||||
'length %r, counter %r', track, index, path, length, counter)
|
||||
'length %r, counter %r', track, index, path, length,
|
||||
counter)
|
||||
|
||||
t = self.tracks[track - 1]
|
||||
i = t.indexes[index]
|
||||
start = i.absolute
|
||||
assert start is not None, "index %r is missing absolute offset" % i
|
||||
end = start + length - 1 # last sector that should come from this file
|
||||
end = start + length - 1 # last sector that should come from this file
|
||||
|
||||
# FIXME: check border conditions here, esp. wrt. toc's off-by-one bug
|
||||
while i.absolute <= end:
|
||||
@@ -703,8 +705,8 @@ class Table(object):
|
||||
i.relative = i.absolute - start
|
||||
i.counter = counter
|
||||
logger.debug('Setting path %r, relative %r on '
|
||||
'track %d, index %d, counter %r',
|
||||
path, i.relative, track, index, counter)
|
||||
'track %d, index %d, counter %r',
|
||||
path, i.relative, track, index, counter)
|
||||
try:
|
||||
track, index = self.getNextTrackIndex(track, index)
|
||||
t = self.tracks[track - 1]
|
||||
@@ -733,10 +735,11 @@ class Table(object):
|
||||
logger.debug('Track %d, index %d has no counter', t, i)
|
||||
break
|
||||
if index.counter != counter:
|
||||
logger.debug('Track %d, index %d has a different counter', t, i)
|
||||
logger.debug(
|
||||
'Track %d, index %d has a different counter', t, i)
|
||||
break
|
||||
logger.debug('Setting absolute offset %d on track %d, index %d',
|
||||
index.relative, t, i)
|
||||
index.relative, t, i)
|
||||
if index.absolute is not None:
|
||||
if index.absolute != index.relative:
|
||||
msg = 'Track %d, index %d had absolute %d,' \
|
||||
@@ -769,15 +772,17 @@ class Table(object):
|
||||
for i in t.indexes.values():
|
||||
if i.absolute is not None:
|
||||
i.absolute += self.leadout + gap
|
||||
logger.debug('Fixing track %02d, index %02d, absolute %d' % (
|
||||
t.number, i.number, i.absolute))
|
||||
logger.debug('Fixing track %02d, index %02d, '
|
||||
'absolute %d' % (
|
||||
t.number, i.number, i.absolute))
|
||||
if i.counter is not None:
|
||||
i.counter += sourceCounter
|
||||
logger.debug('Fixing track %02d, index %02d, counter %d' % (
|
||||
t.number, i.number, i.counter))
|
||||
logger.debug('Fixing track %02d, index %02d, '
|
||||
'counter %d' % (
|
||||
t.number, i.number, i.counter))
|
||||
self.tracks.append(t)
|
||||
|
||||
self.leadout += other.leadout + gap # FIXME
|
||||
self.leadout += other.leadout + gap # FIXME
|
||||
logger.debug('Fixing leadout, now %d', self.leadout)
|
||||
|
||||
def _getSessionGap(self, session):
|
||||
@@ -795,7 +800,7 @@ class Table(object):
|
||||
gap = 6900
|
||||
return gap
|
||||
|
||||
### lookups
|
||||
# lookups
|
||||
|
||||
def getNextTrackIndex(self, track, index):
|
||||
"""
|
||||
@@ -857,8 +862,8 @@ class Table(object):
|
||||
for t in self.tracks:
|
||||
for i in t.indexes.values():
|
||||
if i.relative is None:
|
||||
logger.debug('Track %02d, Index %02d does not have relative',
|
||||
t.number, i.number)
|
||||
logger.debug('Track %02d, Index %02d does not '
|
||||
'have relative', t.number, i.number)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@@ -109,8 +109,8 @@ class Sources:
|
||||
@type counter: int
|
||||
@param offset: the absolute disc offset where this source starts
|
||||
"""
|
||||
logger.debug('Appending source, counter %d, abs offset %d, source %r' % (
|
||||
counter, offset, source))
|
||||
logger.debug('Appending source, counter %d, abs offset %d, '
|
||||
'source %r' % (counter, offset, source))
|
||||
self._sources.append((counter, offset, source))
|
||||
|
||||
def get(self, offset):
|
||||
@@ -158,33 +158,34 @@ class TocFile(object):
|
||||
relative = absolute - counterStart
|
||||
|
||||
currentTrack.index(i, path=s.path,
|
||||
absolute=absolute,
|
||||
relative=relative,
|
||||
counter=c)
|
||||
absolute=absolute,
|
||||
relative=relative,
|
||||
counter=c)
|
||||
logger.debug(
|
||||
'[track %02d index %02d] trackOffset %r, added %r',
|
||||
currentTrack.number, i, trackOffset,
|
||||
currentTrack.getIndex(i))
|
||||
|
||||
currentTrack.number, i, trackOffset,
|
||||
currentTrack.getIndex(i))
|
||||
|
||||
def parse(self):
|
||||
# these two objects start as None then get set as real objects,
|
||||
# so no need to complain about them here
|
||||
__pychecker__ = 'no-objattrs'
|
||||
currentFile = None
|
||||
currentTrack = None
|
||||
|
||||
state = 'HEADER'
|
||||
counter = 0 # counts sources for audio data; SILENCE/ZERO/FILE
|
||||
# counts sources for audio data; SILENCE/ZERO/FILE
|
||||
counter = 0
|
||||
trackNumber = 0
|
||||
indexNumber = 0
|
||||
absoluteOffset = 0 # running absolute offset of where each track starts
|
||||
relativeOffset = 0 # running relative offset, relative to counter src
|
||||
currentLength = 0 # accrued during TRACK record parsing;
|
||||
# length of current track as parsed so far;
|
||||
# reset on each TRACK statement
|
||||
totalLength = 0 # accrued during TRACK record parsing, total disc
|
||||
pregapLength = 0 # length of the pre-gap, current track in for loop
|
||||
# running absolute offset: where each track starts
|
||||
absoluteOffset = 0
|
||||
# running relative offset, relative to counter src
|
||||
relativeOffset = 0
|
||||
# currentLength is accrued during TRACK record parsing length
|
||||
# of current track as parsed so far reset on each TRACK statement
|
||||
currentLength = 0
|
||||
# accrued during TRACK record parsing, total disc
|
||||
totalLength = 0
|
||||
# length of the pre-gap, current track in for loop
|
||||
pregapLength = 0
|
||||
|
||||
# the first track's INDEX 1 can only be gotten from the .toc
|
||||
# file once the first pregap is calculated; so we add INDEX 1
|
||||
@@ -211,9 +212,9 @@ class TocFile(object):
|
||||
logger.debug('Found disc CD-Text %s: %r', key, value)
|
||||
elif state == 'TRACK':
|
||||
if key != 'ISRC' or not currentTrack \
|
||||
or currentTrack.isrc is not None:
|
||||
or currentTrack.isrc is not None:
|
||||
logger.debug('Found track CD-Text %s: %r',
|
||||
key, value)
|
||||
key, value)
|
||||
currentTrack.cdtext[key] = value
|
||||
|
||||
# look for header elements
|
||||
@@ -246,7 +247,7 @@ class TocFile(object):
|
||||
|
||||
# FIXME: track mode
|
||||
logger.debug('found track %d, mode %s, at absoluteOffset %d',
|
||||
trackNumber, trackMode, absoluteOffset)
|
||||
trackNumber, trackMode, absoluteOffset)
|
||||
|
||||
# reset counters relative to a track
|
||||
currentLength = 0
|
||||
@@ -299,18 +300,18 @@ class TocFile(object):
|
||||
start = m.group('start')
|
||||
length = m.group('length')
|
||||
logger.debug('FILE %s, start %r, length %r',
|
||||
filePath, common.msfToFrames(start),
|
||||
common.msfToFrames(length))
|
||||
filePath, common.msfToFrames(start),
|
||||
common.msfToFrames(length))
|
||||
if not currentFile or filePath != currentFile.path:
|
||||
counter += 1
|
||||
relativeOffset = 0
|
||||
logger.debug('track %d, switched to new FILE, '
|
||||
'increased counter to %d',
|
||||
trackNumber, counter)
|
||||
'increased counter to %d',
|
||||
trackNumber, counter)
|
||||
currentFile = File(filePath, common.msfToFrames(start),
|
||||
common.msfToFrames(length))
|
||||
common.msfToFrames(length))
|
||||
self._sources.append(counter, absoluteOffset + currentLength,
|
||||
currentFile)
|
||||
currentFile)
|
||||
currentLength += common.msfToFrames(length)
|
||||
|
||||
# look for DATAFILE lines
|
||||
@@ -319,20 +320,19 @@ class TocFile(object):
|
||||
filePath = m.group('name')
|
||||
length = m.group('length')
|
||||
logger.debug('FILE %s, length %r',
|
||||
filePath, common.msfToFrames(length))
|
||||
filePath, common.msfToFrames(length))
|
||||
if not currentFile or filePath != currentFile.path:
|
||||
counter += 1
|
||||
relativeOffset = 0
|
||||
logger.debug('track %d, switched to new FILE, '
|
||||
'increased counter to %d',
|
||||
trackNumber, counter)
|
||||
'increased counter to %d',
|
||||
trackNumber, counter)
|
||||
# FIXME: assume that a MODE2_FORM_MIX track always starts at 0
|
||||
currentFile = File(filePath, 0, common.msfToFrames(length))
|
||||
self._sources.append(counter, absoluteOffset + currentLength,
|
||||
currentFile)
|
||||
currentFile)
|
||||
currentLength += common.msfToFrames(length)
|
||||
|
||||
|
||||
# look for START lines
|
||||
m = _START_RE.search(line)
|
||||
if m:
|
||||
@@ -349,10 +349,10 @@ class TocFile(object):
|
||||
relativeOffset = absoluteOffset - counterStart
|
||||
|
||||
currentTrack.index(0, path=s and s.path or None,
|
||||
absolute=absoluteOffset,
|
||||
relative=relativeOffset, counter=c)
|
||||
absolute=absoluteOffset,
|
||||
relative=relativeOffset, counter=c)
|
||||
logger.debug('[track %02d index 00] added %r',
|
||||
currentTrack.number, currentTrack.getIndex(0))
|
||||
currentTrack.number, currentTrack.getIndex(0))
|
||||
# store the pregapLength to add it when we index 1 for this
|
||||
# track on the next iteration
|
||||
pregapLength = length
|
||||
@@ -398,8 +398,8 @@ class TocFile(object):
|
||||
# last track, so no length known
|
||||
return -1
|
||||
|
||||
thisIndex = track.indexes[1] # FIXME: could be more
|
||||
nextIndex = self.table.tracks[i + 1].indexes[1] # FIXME: could be 0
|
||||
thisIndex = track.indexes[1] # FIXME: could be more
|
||||
nextIndex = self.table.tracks[i + 1].indexes[1] # FIXME: could be 0
|
||||
|
||||
c = thisIndex.counter
|
||||
if c is not None and c == nextIndex.counter:
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
from os.path import exists
|
||||
from subprocess import Popen, PIPE
|
||||
|
||||
import logging
|
||||
@@ -7,6 +6,7 @@ logger = logging.getLogger(__name__)
|
||||
ARB = 'accuraterip-checksum'
|
||||
FLAC = 'flac'
|
||||
|
||||
|
||||
def accuraterip_checksum(f, track, tracks, wave=False, v2=False):
|
||||
v = '--accuraterip-v1'
|
||||
if v2:
|
||||
|
||||
@@ -79,12 +79,12 @@ _ERROR_RE = re.compile("^scsi_read error:")
|
||||
|
||||
|
||||
class ProgressParser:
|
||||
read = 0 # last [read] frame
|
||||
wrote = 0 # last [wrote] frame
|
||||
errors = 0 # count of number of scsi errors
|
||||
_nframes = None # number of frames read on each [read]
|
||||
_firstFrames = None # number of frames read on first [read]
|
||||
reads = 0 # total number of reads
|
||||
read = 0 # last [read] frame
|
||||
wrote = 0 # last [wrote] frame
|
||||
errors = 0 # count of number of scsi errors
|
||||
_nframes = None # number of frames read on each [read]
|
||||
_firstFrames = None # number of frames read on first [read]
|
||||
reads = 0 # total number of reads
|
||||
|
||||
def __init__(self, start, stop):
|
||||
"""
|
||||
@@ -99,7 +99,7 @@ class ProgressParser:
|
||||
# FIXME: privatize
|
||||
self.read = start
|
||||
|
||||
self._reads = {} # read count for each sector
|
||||
self._reads = {} # read count for each sector
|
||||
|
||||
def parse(self, line):
|
||||
"""
|
||||
@@ -121,8 +121,7 @@ class ProgressParser:
|
||||
|
||||
def _parse_read(self, wordOffset):
|
||||
if wordOffset % common.WORDS_PER_FRAME != 0:
|
||||
logger.debug(
|
||||
'THOMAS: not a multiple of %d: %d' % (
|
||||
logger.debug('THOMAS: not a multiple of %d: %d' % (
|
||||
common.WORDS_PER_FRAME, wordOffset))
|
||||
return
|
||||
|
||||
@@ -139,7 +138,7 @@ class ProgressParser:
|
||||
logger.debug('set firstFrames to %r', self._firstFrames)
|
||||
|
||||
markStart = None
|
||||
markEnd = None # the next unread frame (half-inclusive)
|
||||
markEnd = None # the next unread frame (half-inclusive)
|
||||
|
||||
# verify it either read nframes more or went back for verify
|
||||
if frameOffset > self.read:
|
||||
@@ -156,13 +155,13 @@ class ProgressParser:
|
||||
# we could use firstFrames as an estimate on how many frames this
|
||||
# read, but this lowers our track quality needlessly where
|
||||
# EAC still reports 100% track quality
|
||||
markStart = frameOffset # - self._firstFrames
|
||||
markStart = frameOffset # - self._firstFrames
|
||||
markEnd = frameOffset
|
||||
|
||||
# FIXME: doing this is way too slow even for a testcase, so disable
|
||||
if False:
|
||||
for frame in range(markStart, markEnd):
|
||||
if not frame in self._reads.keys():
|
||||
if frame not in self._reads.keys():
|
||||
self._reads[frame] = 0
|
||||
self._reads[frame] += 1
|
||||
|
||||
@@ -189,7 +188,7 @@ class ProgressParser:
|
||||
Each frame gets read twice.
|
||||
More than two reads for a frame reduce track quality.
|
||||
"""
|
||||
frames = self.stop - self.start + 1 # + 1 since stop is inclusive
|
||||
frames = self.stop - self.start + 1 # + 1 since stop is inclusive
|
||||
reads = self.reads
|
||||
logger.debug('getTrackQuality: frames %d, reads %d' % (frames, reads))
|
||||
|
||||
@@ -209,14 +208,14 @@ class ReadTrackTask(task.Task):
|
||||
"""
|
||||
|
||||
description = "Reading track"
|
||||
quality = None # set at end of reading
|
||||
quality = None # set at end of reading
|
||||
speed = None
|
||||
duration = None # in seconds
|
||||
duration = None # in seconds
|
||||
|
||||
_MAXERROR = 100 # number of errors detected by parser
|
||||
_MAXERROR = 100 # number of errors detected by parser
|
||||
|
||||
def __init__(self, path, table, start, stop, overread, offset=0,
|
||||
device=None, action="Reading", what="track"):
|
||||
device=None, action="Reading", what="track"):
|
||||
"""
|
||||
Read the given track.
|
||||
|
||||
@@ -249,7 +248,7 @@ class ReadTrackTask(task.Task):
|
||||
self._start_time = None
|
||||
self._overread = overread
|
||||
|
||||
self._buffer = "" # accumulate characters
|
||||
self._buffer = "" # accumulate characters
|
||||
self._errors = []
|
||||
self.description = "%s %s" % (action, what)
|
||||
|
||||
@@ -271,31 +270,33 @@ class ReadTrackTask(task.Task):
|
||||
stopOffset = self._stop - self._table.getTrackStart(i + 1)
|
||||
|
||||
logger.debug('Ripping from %d to %d (inclusive)',
|
||||
self._start, self._stop)
|
||||
self._start, self._stop)
|
||||
logger.debug('Starting at track %d, offset %d',
|
||||
startTrack, startOffset)
|
||||
startTrack, startOffset)
|
||||
logger.debug('Stopping at track %d, offset %d',
|
||||
stopTrack, stopOffset)
|
||||
stopTrack, stopOffset)
|
||||
|
||||
bufsize = 1024
|
||||
if self._overread:
|
||||
argv = ["cdparanoia", "--stderr-progress",
|
||||
"--sample-offset=%d" % self._offset, "--force-overread", ]
|
||||
"--sample-offset=%d" % self._offset, "--force-overread", ]
|
||||
else:
|
||||
argv = ["cdparanoia", "--stderr-progress",
|
||||
"--sample-offset=%d" % self._offset, ]
|
||||
"--sample-offset=%d" % self._offset, ]
|
||||
if self._device:
|
||||
argv.extend(["--force-cdrom-device", self._device, ])
|
||||
argv.extend(["%d[%s]-%d[%s]" % (
|
||||
startTrack, common.framesToHMSF(startOffset),
|
||||
stopTrack, common.framesToHMSF(stopOffset)),
|
||||
startTrack, common.framesToHMSF(startOffset),
|
||||
stopTrack, common.framesToHMSF(stopOffset)),
|
||||
self.path])
|
||||
logger.debug('Running %s' % (" ".join(argv), ))
|
||||
try:
|
||||
self._popen = asyncsub.Popen(argv,
|
||||
bufsize=bufsize,
|
||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE, close_fds=True)
|
||||
bufsize=bufsize,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
close_fds=True)
|
||||
except OSError, e:
|
||||
import errno
|
||||
if e.errno == errno.ENOENT:
|
||||
@@ -366,7 +367,7 @@ class ReadTrackTask(task.Task):
|
||||
if size != expected:
|
||||
# FIXME: handle errors better
|
||||
logger.warning('file size %d did not match expected size %d',
|
||||
size, expected)
|
||||
size, expected)
|
||||
if (size - expected) % common.BYTES_PER_FRAME == 0:
|
||||
logger.warning('%d frames difference' % (
|
||||
(size - expected) / common.BYTES_PER_FRAME))
|
||||
@@ -374,8 +375,10 @@ class ReadTrackTask(task.Task):
|
||||
logger.warning('non-integral amount of frames difference')
|
||||
|
||||
self.setAndRaiseException(FileSizeError(self.path,
|
||||
"File size %d did not match expected size %d" % (
|
||||
size, expected)))
|
||||
"File size %d did not "
|
||||
"match expected "
|
||||
"size %d" % (
|
||||
size, expected)))
|
||||
|
||||
if not self.exception and self._popen.returncode != 0:
|
||||
if self._errors:
|
||||
@@ -461,10 +464,11 @@ class ReadVerifyTrackTask(task.MultiSeparateTask):
|
||||
self.tasks = []
|
||||
self.tasks.append(
|
||||
ReadTrackTask(tmppath, table, start, stop, overread,
|
||||
offset=offset, device=device, what=what))
|
||||
offset=offset, device=device, what=what))
|
||||
self.tasks.append(checksum.CRC32Task(tmppath))
|
||||
t = ReadTrackTask(tmppath, table, start, stop, overread,
|
||||
offset=offset, device=device, action="Verifying", what=what)
|
||||
offset=offset, device=device, action="Verifying",
|
||||
what=what)
|
||||
self.tasks.append(t)
|
||||
self.tasks.append(checksum.CRC32Task(tmppath))
|
||||
|
||||
@@ -503,7 +507,7 @@ class ReadVerifyTrackTask(task.MultiSeparateTask):
|
||||
try:
|
||||
if not self.exception:
|
||||
self.quality = max(self.tasks[0].quality,
|
||||
self.tasks[2].quality)
|
||||
self.tasks[2].quality)
|
||||
self.peak = self.tasks[6].peak
|
||||
logger.debug('peak: %r', self.peak)
|
||||
self.testspeed = self.tasks[0].speed
|
||||
@@ -535,9 +539,8 @@ class ReadVerifyTrackTask(task.MultiSeparateTask):
|
||||
logger.debug('Moving to final path %r', self.path)
|
||||
os.rename(self._tmppath, self.path)
|
||||
except Exception, e:
|
||||
logger.debug('Exception while moving to final path %r: '
|
||||
'%r',
|
||||
self.path, str(e))
|
||||
logger.debug('Exception while moving to final '
|
||||
'path %r: %r', self.path, str(e))
|
||||
self.exception = e
|
||||
else:
|
||||
os.unlink(self._tmppath)
|
||||
@@ -548,15 +551,16 @@ class ReadVerifyTrackTask(task.MultiSeparateTask):
|
||||
|
||||
task.MultiSeparateTask.stop(self)
|
||||
|
||||
|
||||
_VERSION_RE = re.compile(
|
||||
"^cdparanoia (?P<version>.+) release (?P<release>.+) \(.*\)")
|
||||
|
||||
|
||||
def getCdParanoiaVersion():
|
||||
getter = common.VersionGetter('cdparanoia',
|
||||
["cdparanoia", "-V"],
|
||||
_VERSION_RE,
|
||||
"%(version)s %(release)s")
|
||||
["cdparanoia", "-V"],
|
||||
_VERSION_RE,
|
||||
"%(version)s %(release)s")
|
||||
|
||||
return getter.get()
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
CDRDAO = 'cdrdao'
|
||||
|
||||
|
||||
def read_toc(device, fast_toc=False):
|
||||
"""
|
||||
Return cdrdao-generated table of contents for 'device'.
|
||||
@@ -26,7 +27,7 @@ def read_toc(device, fast_toc=False):
|
||||
os.unlink(tocfile)
|
||||
|
||||
cmd = [CDRDAO, 'read-toc'] + (['--fast-toc'] if fast_toc else []) + [
|
||||
'--device', device, tocfile]
|
||||
'--device', device, tocfile]
|
||||
# PIPE is the closest to >/dev/null we can get
|
||||
logger.debug("executing %r", cmd)
|
||||
p = Popen(cmd, stdout=PIPE, stderr=PIPE)
|
||||
@@ -45,6 +46,7 @@ def read_toc(device, fast_toc=False):
|
||||
os.unlink(tocfile)
|
||||
return toc
|
||||
|
||||
|
||||
def version():
|
||||
"""
|
||||
Return cdrdao version as a string.
|
||||
@@ -56,25 +58,28 @@ def version():
|
||||
"return code is " + str(cdrdao.returncode))
|
||||
return None
|
||||
m = re.compile(r'^Cdrdao version (?P<version>.*) - \(C\)').search(
|
||||
err.decode('utf-8'))
|
||||
err.decode('utf-8'))
|
||||
if not m:
|
||||
logger.warning("cdrdao version detection failed: "
|
||||
"could not find version")
|
||||
return None
|
||||
return m.group('version')
|
||||
|
||||
|
||||
def ReadTOCTask(device):
|
||||
"""
|
||||
stopgap morituri-insanity compatibility layer
|
||||
"""
|
||||
return read_toc(device, fast_toc=True)
|
||||
|
||||
|
||||
def ReadTableTask(device):
|
||||
"""
|
||||
stopgap morituri-insanity compatibility layer
|
||||
"""
|
||||
return read_toc(device)
|
||||
|
||||
|
||||
def getCDRDAOVersion():
|
||||
"""
|
||||
stopgap morituri-insanity compatibility layer
|
||||
|
||||
@@ -3,6 +3,7 @@ from subprocess import check_call, CalledProcessError
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def encode(infile, outfile):
|
||||
"""
|
||||
Encodes infile to outfile, with flac.
|
||||
|
||||
@@ -6,6 +6,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
SOX = 'sox'
|
||||
|
||||
|
||||
def peak_level(track_path):
|
||||
"""
|
||||
Accepts a path to a sox-decodable audio file.
|
||||
|
||||
@@ -8,6 +8,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
SOXI = 'soxi'
|
||||
|
||||
|
||||
class AudioLengthTask(ctask.PopenTask):
|
||||
"""
|
||||
I calculate the length of a track in audio samples.
|
||||
@@ -41,7 +42,7 @@ class AudioLengthTask(ctask.PopenTask):
|
||||
self._error.append(bytes)
|
||||
|
||||
def failed(self):
|
||||
self.setException(Exception("soxi failed: %s"%"".join(self._error)))
|
||||
self.setException(Exception("soxi failed: %s" % "".join(self._error)))
|
||||
|
||||
def done(self):
|
||||
if self._error:
|
||||
|
||||
@@ -131,7 +131,7 @@ class WhipperLogger(result.Logger):
|
||||
accurateTracks = nonHTOA - self._accuratelyRipped
|
||||
lines.append("%s Some tracks could not be verified as "
|
||||
"accurate (%d/%d got no match)" % (
|
||||
arHeading, accurateTracks, nonHTOA))
|
||||
arHeading, accurateTracks, nonHTOA))
|
||||
else:
|
||||
lines.append("%s All tracks accurately ripped" % arHeading)
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ class TrackResult:
|
||||
"""
|
||||
number = None
|
||||
filename = None
|
||||
pregap = 0 # in frames
|
||||
pregap = 0 # in frames
|
||||
pre_emphasis = None
|
||||
|
||||
peak = 0.0
|
||||
@@ -65,7 +65,7 @@ class TrackResult:
|
||||
copyduration = 0.0
|
||||
testcrc = None
|
||||
copycrc = None
|
||||
accurip = False # whether it's in the database
|
||||
accurip = False # whether it's in the database
|
||||
ARCRC = None
|
||||
ARDBCRC = None
|
||||
ARDBConfidence = None
|
||||
|
||||
@@ -53,12 +53,12 @@ class TestCase(unittest.TestCase):
|
||||
return inst
|
||||
except exception, e:
|
||||
raise Exception('%s raised instead of %s:\n %s' %
|
||||
(sys.exec_info()[0], exception.__name__, str(e))
|
||||
)
|
||||
(sys.exec_info()[0], exception.__name__, str(e))
|
||||
)
|
||||
else:
|
||||
raise Exception('%s not raised (%r returned)' %
|
||||
(exception.__name__, result)
|
||||
)
|
||||
(exception.__name__, result)
|
||||
)
|
||||
|
||||
assertRaises = failUnlessRaises
|
||||
|
||||
@@ -67,8 +67,8 @@ class TestCase(unittest.TestCase):
|
||||
Read a .cue file, and replace the version comment with the current
|
||||
version so we can use it in comparisons.
|
||||
"""
|
||||
ret = open(os.path.join(os.path.dirname(__file__), name)).read(
|
||||
).decode('utf-8')
|
||||
cuefile = os.path.join(os.path.dirname(__file__), name)
|
||||
ret = open(cuefile).read().decode('utf-8')
|
||||
ret = re.sub(
|
||||
'REM COMMENT "whipper.*',
|
||||
'REM COMMENT "whipper %s"' % (whipper.__version__),
|
||||
@@ -76,6 +76,7 @@ class TestCase(unittest.TestCase):
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
class UnicodeTestMixin:
|
||||
# A helper mixin to skip tests if we're not in a UTF-8 locale
|
||||
|
||||
|
||||
@@ -12,13 +12,12 @@ class AccurateRipResponseTestCase(tcommon.TestCase):
|
||||
|
||||
def testResponse(self):
|
||||
path = os.path.join(os.path.dirname(__file__),
|
||||
'dBAR-011-0010e284-009228a3-9809ff0b.bin')
|
||||
'dBAR-011-0010e284-009228a3-9809ff0b.bin')
|
||||
data = open(path, "rb").read()
|
||||
|
||||
responses = accurip.getAccurateRipResponses(data)
|
||||
self.assertEquals(len(responses), 3)
|
||||
|
||||
|
||||
response = responses[0]
|
||||
|
||||
self.assertEquals(response.trackCount, 11)
|
||||
|
||||
@@ -13,12 +13,12 @@ class ShrinkTestCase(tcommon.TestCase):
|
||||
|
||||
def testSufjan(self):
|
||||
path = (u'whipper/Sufjan Stevens - Illinois/02. Sufjan Stevens - '
|
||||
'The Black Hawk War, or, How to Demolish an Entire '
|
||||
'Civilization and Still Feel Good About Yourself in the '
|
||||
'Morning, or, We Apologize for the Inconvenience but '
|
||||
'You\'re Going to Have to Leave Now, or, "I Have Fought '
|
||||
'the Big Knives and Will Continue to Fight Them Until They '
|
||||
'Are Off Our Lands!".flac')
|
||||
'The Black Hawk War, or, How to Demolish an Entire '
|
||||
'Civilization and Still Feel Good About Yourself in the '
|
||||
'Morning, or, We Apologize for the Inconvenience but '
|
||||
'You\'re Going to Have to Leave Now, or, "I Have Fought '
|
||||
'the Big Knives and Will Continue to Fight Them Until They '
|
||||
'Are Off Our Lands!".flac')
|
||||
|
||||
shorter = common.shrinkPath(path)
|
||||
self.failUnless(os.path.splitext(path)[0].startswith(
|
||||
@@ -46,7 +46,7 @@ class GetRelativePathTestCase(tcommon.TestCase):
|
||||
track = './' + directory + '/01. Placebo - Taste in Men.flac'
|
||||
|
||||
self.assertEquals(common.getRelativePath(track, cue),
|
||||
'01. Placebo - Taste in Men.flac')
|
||||
'01. Placebo - Taste in Men.flac')
|
||||
|
||||
|
||||
class GetRealPathTestCase(tcommon.TestCase):
|
||||
@@ -56,12 +56,12 @@ class GetRealPathTestCase(tcommon.TestCase):
|
||||
refPath = os.path.join(os.path.dirname(path), 'fake.cue')
|
||||
|
||||
self.assertEquals(common.getRealPath(refPath, path),
|
||||
path)
|
||||
path)
|
||||
|
||||
# same path, but with wav extension, will point to flac file
|
||||
wavPath = path[:-4] + 'wav'
|
||||
self.assertEquals(common.getRealPath(refPath, wavPath),
|
||||
path)
|
||||
path)
|
||||
|
||||
os.close(fd)
|
||||
os.unlink(path)
|
||||
|
||||
@@ -20,24 +20,24 @@ class ConfigTestCase(tcommon.TestCase):
|
||||
os.unlink(self._path)
|
||||
|
||||
def testAddReadOffset(self):
|
||||
self.assertRaises(KeyError,
|
||||
self._config.getReadOffset, 'PLEXTOR ', 'DVDR PX-L890SA', '1.05')
|
||||
self.assertRaises(KeyError, self._config.getReadOffset,
|
||||
'PLEXTOR ', 'DVDR PX-L890SA', '1.05')
|
||||
self._config.setReadOffset('PLEXTOR ', 'DVDR PX-L890SA', '1.05', 6)
|
||||
|
||||
# getting it from memory should work
|
||||
offset = self._config.getReadOffset('PLEXTOR ', 'DVDR PX-L890SA',
|
||||
'1.05')
|
||||
offset = self._config.getReadOffset(
|
||||
'PLEXTOR ', 'DVDR PX-L890SA', '1.05')
|
||||
self.assertEquals(offset, 6)
|
||||
|
||||
# and so should getting it after reading it again
|
||||
self._config.open()
|
||||
offset = self._config.getReadOffset('PLEXTOR ', 'DVDR PX-L890SA',
|
||||
'1.05')
|
||||
offset = self._config.getReadOffset(
|
||||
'PLEXTOR ', 'DVDR PX-L890SA', '1.05')
|
||||
self.assertEquals(offset, 6)
|
||||
|
||||
def testAddReadOffsetSpaced(self):
|
||||
self.assertRaises(KeyError,
|
||||
self._config.getReadOffset, 'Slimtype', 'eSAU208 2 ', 'ML03')
|
||||
self.assertRaises(KeyError, self._config.getReadOffset,
|
||||
'Slimtype', 'eSAU208 2 ', 'ML03')
|
||||
self._config.setReadOffset('Slimtype', 'eSAU208 2 ', 'ML03', 6)
|
||||
|
||||
# getting it from memory should work
|
||||
@@ -53,7 +53,7 @@ class ConfigTestCase(tcommon.TestCase):
|
||||
|
||||
def testDefeatsCache(self):
|
||||
self.assertRaises(KeyError, self._config.getDefeatsCache,
|
||||
'PLEXTOR ', 'DVDR PX-L890SA', '1.05')
|
||||
'PLEXTOR ', 'DVDR PX-L890SA', '1.05')
|
||||
|
||||
self._config.setDefeatsCache(
|
||||
'PLEXTOR ', 'DVDR PX-L890SA', '1.05', False)
|
||||
|
||||
@@ -13,8 +13,8 @@ class MetadataTestCase(unittest.TestCase):
|
||||
|
||||
# Generated with rip -R cd info
|
||||
def testJeffEverybodySingle(self):
|
||||
path = os.path.join(os.path.dirname(__file__),
|
||||
'whipper.release.3451f29c-9bb8-4cc5-bfcc-bd50104b94f8.json')
|
||||
filename = 'whipper.release.3451f29c-9bb8-4cc5-bfcc-bd50104b94f8.json'
|
||||
path = os.path.join(os.path.dirname(__file__), filename)
|
||||
handle = open(path, "rb")
|
||||
response = json.loads(handle.read())
|
||||
handle.close()
|
||||
@@ -26,8 +26,8 @@ class MetadataTestCase(unittest.TestCase):
|
||||
|
||||
def test2MeterSessies10(self):
|
||||
# various artists, multiple artists per track
|
||||
path = os.path.join(os.path.dirname(__file__),
|
||||
'whipper.release.a76714e0-32b1-4ed4-b28e-f86d99642193.json')
|
||||
filename = 'whipper.release.a76714e0-32b1-4ed4-b28e-f86d99642193.json'
|
||||
path = os.path.join(os.path.dirname(__file__), filename)
|
||||
handle = open(path, "rb")
|
||||
response = json.loads(handle.read())
|
||||
handle.close()
|
||||
@@ -38,7 +38,7 @@ class MetadataTestCase(unittest.TestCase):
|
||||
self.assertEquals(metadata.artist, u'Various Artists')
|
||||
self.assertEquals(metadata.release, u'2001-10-15')
|
||||
self.assertEquals(metadata.mbidArtist,
|
||||
u'89ad4ac3-39f7-470e-963a-56509c546377')
|
||||
u'89ad4ac3-39f7-470e-963a-56509c546377')
|
||||
|
||||
self.assertEquals(len(metadata.tracks), 18)
|
||||
|
||||
@@ -46,16 +46,16 @@ class MetadataTestCase(unittest.TestCase):
|
||||
|
||||
self.assertEquals(track16.artist, 'Tom Jones & Stereophonics')
|
||||
self.assertEquals(track16.mbidArtist,
|
||||
u'57c6f649-6cde-48a7-8114-2a200247601a'
|
||||
';0bfba3d3-6a04-4779-bb0a-df07df5b0558'
|
||||
)
|
||||
u'57c6f649-6cde-48a7-8114-2a200247601a'
|
||||
';0bfba3d3-6a04-4779-bb0a-df07df5b0558'
|
||||
)
|
||||
self.assertEquals(track16.sortName,
|
||||
u'Jones, Tom & Stereophonics')
|
||||
u'Jones, Tom & Stereophonics')
|
||||
|
||||
def testBalladOfTheBrokenSeas(self):
|
||||
# various artists disc
|
||||
path = os.path.join(os.path.dirname(__file__),
|
||||
'whipper.release.e32ae79a-336e-4d33-945c-8c5e8206dbd3.json')
|
||||
filename = 'whipper.release.e32ae79a-336e-4d33-945c-8c5e8206dbd3.json'
|
||||
path = os.path.join(os.path.dirname(__file__), filename)
|
||||
handle = open(path, "rb")
|
||||
response = json.loads(handle.read())
|
||||
handle.close()
|
||||
@@ -65,11 +65,11 @@ class MetadataTestCase(unittest.TestCase):
|
||||
|
||||
self.assertEquals(metadata.artist, u'Isobel Campbell & Mark Lanegan')
|
||||
self.assertEquals(metadata.sortName,
|
||||
u'Campbell, Isobel & Lanegan, Mark')
|
||||
u'Campbell, Isobel & Lanegan, Mark')
|
||||
self.assertEquals(metadata.release, u'2006-01-30')
|
||||
self.assertEquals(metadata.mbidArtist,
|
||||
u'd51f3a15-12a2-41a0-acfa-33b5eae71164;'
|
||||
'a9126556-f555-4920-9617-6e013f8228a7')
|
||||
u'd51f3a15-12a2-41a0-acfa-33b5eae71164;'
|
||||
'a9126556-f555-4920-9617-6e013f8228a7')
|
||||
|
||||
self.assertEquals(len(metadata.tracks), 12)
|
||||
|
||||
@@ -77,18 +77,18 @@ class MetadataTestCase(unittest.TestCase):
|
||||
|
||||
self.assertEquals(track12.artist, u'Isobel Campbell & Mark Lanegan')
|
||||
self.assertEquals(track12.sortName,
|
||||
u'Campbell, Isobel'
|
||||
' & Lanegan, Mark'
|
||||
)
|
||||
u'Campbell, Isobel'
|
||||
' & Lanegan, Mark'
|
||||
)
|
||||
self.assertEquals(track12.mbidArtist,
|
||||
u'd51f3a15-12a2-41a0-acfa-33b5eae71164;'
|
||||
'a9126556-f555-4920-9617-6e013f8228a7')
|
||||
u'd51f3a15-12a2-41a0-acfa-33b5eae71164;'
|
||||
'a9126556-f555-4920-9617-6e013f8228a7')
|
||||
|
||||
def testMalaInCuba(self):
|
||||
# single artist disc, but with multiple artists tracks
|
||||
# see https://github.com/thomasvs/morituri/issues/19
|
||||
path = os.path.join(os.path.dirname(__file__),
|
||||
'whipper.release.61c6fd9b-18f8-4a45-963a-ba3c5d990cae.json')
|
||||
filename = 'whipper.release.61c6fd9b-18f8-4a45-963a-ba3c5d990cae.json'
|
||||
path = os.path.join(os.path.dirname(__file__), filename)
|
||||
handle = open(path, "rb")
|
||||
response = json.loads(handle.read())
|
||||
handle.close()
|
||||
@@ -100,7 +100,7 @@ class MetadataTestCase(unittest.TestCase):
|
||||
self.assertEquals(metadata.sortName, u'Mala')
|
||||
self.assertEquals(metadata.release, u'2012-09-17')
|
||||
self.assertEquals(metadata.mbidArtist,
|
||||
u'09f221eb-c97e-4da5-ac22-d7ab7c555bbb')
|
||||
u'09f221eb-c97e-4da5-ac22-d7ab7c555bbb')
|
||||
|
||||
self.assertEquals(len(metadata.tracks), 14)
|
||||
|
||||
@@ -108,9 +108,9 @@ class MetadataTestCase(unittest.TestCase):
|
||||
|
||||
self.assertEquals(track6.artist, u'Mala feat. Dreiser & Sexto Sentido')
|
||||
self.assertEquals(track6.sortName,
|
||||
u'Mala feat. Dreiser & Sexto Sentido')
|
||||
u'Mala feat. Dreiser & Sexto Sentido')
|
||||
self.assertEquals(track6.mbidArtist,
|
||||
u'09f221eb-c97e-4da5-ac22-d7ab7c555bbb'
|
||||
';ec07a209-55ff-4084-bc41-9d4d1764e075'
|
||||
';f626b92e-07b1-4a19-ad13-c09d690db66c'
|
||||
)
|
||||
u'09f221eb-c97e-4da5-ac22-d7ab7c555bbb'
|
||||
';ec07a209-55ff-4084-bc41-9d4d1764e075'
|
||||
';f626b92e-07b1-4a19-ad13-c09d690db66c'
|
||||
)
|
||||
|
||||
@@ -22,9 +22,9 @@ class FilterTestCase(common.TestCase):
|
||||
def testSpecial(self):
|
||||
part = u'<<< $&*!\' "()`{}[]spaceship>>>'
|
||||
self.assertEquals(self._filter.filter(part),
|
||||
u'___ _____ ________spaceship___')
|
||||
u'___ _____ ________spaceship___')
|
||||
|
||||
def testGreatest(self):
|
||||
part = u'Greatest Ever! Soul: The Definitive Collection'
|
||||
self.assertEquals(self._filter.filter(part),
|
||||
u'Greatest Ever_ Soul - The Definitive Collection')
|
||||
u'Greatest Ever_ Soul - The Definitive Collection')
|
||||
|
||||
@@ -19,15 +19,16 @@ class TrackImageVerifyTestCase(unittest.TestCase):
|
||||
|
||||
def testVerify(self):
|
||||
path = os.path.join(os.path.dirname(__file__),
|
||||
'dBAR-020-002e5023-029d8e49-040eaa14.bin')
|
||||
'dBAR-020-002e5023-029d8e49-040eaa14.bin')
|
||||
data = open(path, "rb").read()
|
||||
responses = accurip.getAccurateRipResponses(data)
|
||||
|
||||
# these crc's were calculated from an actual rip
|
||||
checksums = [1644890007, 2945205445, 3983436658, 1528082495,
|
||||
1203704270, 1163423644, 3649097244, 100524219, 1583356174, 373652058,
|
||||
1842579359, 2850056507, 1329730252, 2526965856, 2525886806, 209743350,
|
||||
3184062337, 2099956663, 2943874164, 2321637196]
|
||||
1203704270, 1163423644, 3649097244, 100524219,
|
||||
1583356174, 373652058, 1842579359, 2850056507,
|
||||
1329730252, 2526965856, 2525886806, 209743350,
|
||||
3184062337, 2099956663, 2943874164, 2321637196]
|
||||
|
||||
prog = program.Program(config.Config())
|
||||
prog.result = result.RipResult()
|
||||
@@ -59,21 +60,21 @@ class TrackImageVerifyTestCase(unittest.TestCase):
|
||||
|
||||
res = prog.getAccurateRipResults()
|
||||
self.assertEquals(res[1 - 1],
|
||||
"Track 1: rip NOT accurate (not found) "
|
||||
"[620b0797], DB [notfound]")
|
||||
"Track 1: rip NOT accurate (not found) "
|
||||
"[620b0797], DB [notfound]")
|
||||
self.assertEquals(res[2 - 1],
|
||||
"Track 2: rip accurate (max confidence 2) "
|
||||
"[af8c44c5], DB [af8c44c5]")
|
||||
"Track 2: rip accurate (max confidence 2) "
|
||||
"[af8c44c5], DB [af8c44c5]")
|
||||
self.assertEquals(res[10 - 1],
|
||||
"Track 10: rip NOT accurate (max confidence 2) "
|
||||
"[16457a5a], DB [eb6e55b4]")
|
||||
"Track 10: rip NOT accurate (max confidence 2) "
|
||||
"[16457a5a], DB [eb6e55b4]")
|
||||
|
||||
|
||||
class HTOATestCase(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
path = os.path.join(os.path.dirname(__file__),
|
||||
'silentalarm.result.pickle')
|
||||
'silentalarm.result.pickle')
|
||||
self._tracks = pickle.load(open(path, 'rb'))
|
||||
|
||||
def testGetAccurateRipResults(self):
|
||||
@@ -90,9 +91,10 @@ class PathTestCase(unittest.TestCase):
|
||||
prog = program.Program(config.Config())
|
||||
|
||||
path = prog.getPath(u'/tmp', DEFAULT_DISC_TEMPLATE,
|
||||
'mbdiscid', 0)
|
||||
'mbdiscid', 0)
|
||||
self.assertEquals(path,
|
||||
u'/tmp/unknown/Unknown Artist - mbdiscid/Unknown Artist - mbdiscid')
|
||||
unicode('/tmp/unknown/Unknown Artist - mbdiscid/'
|
||||
'Unknown Artist - mbdiscid'))
|
||||
|
||||
def testStandardTemplateFilled(self):
|
||||
prog = program.Program(config.Config())
|
||||
@@ -102,9 +104,10 @@ class PathTestCase(unittest.TestCase):
|
||||
prog.metadata = md
|
||||
|
||||
path = prog.getPath(u'/tmp', DEFAULT_DISC_TEMPLATE,
|
||||
'mbdiscid', 0)
|
||||
'mbdiscid', 0)
|
||||
self.assertEquals(path,
|
||||
u'/tmp/unknown/Jeff Buckley - Grace/Jeff Buckley - Grace')
|
||||
unicode('/tmp/unknown/Jeff Buckley - Grace/'
|
||||
'Jeff Buckley - Grace'))
|
||||
|
||||
def testIssue66TemplateFilled(self):
|
||||
prog = program.Program(config.Config())
|
||||
@@ -115,4 +118,4 @@ class PathTestCase(unittest.TestCase):
|
||||
|
||||
path = prog.getPath(u'/tmp', u'%A/%d', 'mbdiscid', 0)
|
||||
self.assertEquals(path,
|
||||
u'/tmp/Jeff Buckley/Grace')
|
||||
u'/tmp/Jeff Buckley/Grace')
|
||||
|
||||
@@ -11,11 +11,12 @@ from whipper.image import table, cue
|
||||
|
||||
from whipper.test import common
|
||||
|
||||
|
||||
class KingsSingleTestCase(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.cue = cue.CueFile(os.path.join(os.path.dirname(__file__),
|
||||
u'kings-single.cue'))
|
||||
u'kings-single.cue'))
|
||||
self.cue.parse()
|
||||
self.assertEquals(len(self.cue.table.tracks), 11)
|
||||
|
||||
@@ -31,7 +32,7 @@ class KingsSeparateTestCase(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.cue = cue.CueFile(os.path.join(os.path.dirname(__file__),
|
||||
u'kings-separate.cue'))
|
||||
u'kings-separate.cue'))
|
||||
self.cue.parse()
|
||||
self.assertEquals(len(self.cue.table.tracks), 11)
|
||||
|
||||
@@ -47,7 +48,7 @@ class KanyeMixedTestCase(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.cue = cue.CueFile(os.path.join(os.path.dirname(__file__),
|
||||
u'kanye.cue'))
|
||||
u'kanye.cue'))
|
||||
self.cue.parse()
|
||||
self.assertEquals(len(self.cue.table.tracks), 13)
|
||||
|
||||
@@ -70,7 +71,7 @@ class WriteCueFileTestCase(unittest.TestCase):
|
||||
|
||||
t = table.Track(2)
|
||||
t.index(0, absolute=1000, path=u'track01.wav',
|
||||
relative=1000, counter=1)
|
||||
relative=1000, counter=1)
|
||||
t.index(1, absolute=2000, path=u'track02.wav', relative=0, counter=2)
|
||||
it.tracks.append(t)
|
||||
it.absolutize()
|
||||
|
||||
@@ -36,7 +36,7 @@ class LadyhawkeTestCase(tcommon.TestCase):
|
||||
self.table.tracks.append(table.Track(13, audio=False))
|
||||
|
||||
offsets = [0, 15537, 31691, 50866, 66466, 81202, 99409,
|
||||
115920, 133093, 149847, 161560, 177682, 207106]
|
||||
115920, 133093, 149847, 161560, 177682, 207106]
|
||||
t = self.table.tracks
|
||||
for i, offset in enumerate(offsets):
|
||||
t[i].index(1, absolute=offset)
|
||||
@@ -59,14 +59,14 @@ class LadyhawkeTestCase(tcommon.TestCase):
|
||||
# however, not (yet) in MusicBrainz database
|
||||
|
||||
self.assertEquals(self.table.getMusicBrainzDiscId(),
|
||||
"KnpGsLhvH.lPrNc1PBL21lb9Bg4-")
|
||||
"KnpGsLhvH.lPrNc1PBL21lb9Bg4-")
|
||||
|
||||
def testAccurateRip(self):
|
||||
self.assertEquals(self.table.getAccurateRipIds(), (
|
||||
"0013bd5a", "00b8d489"))
|
||||
self.assertEquals(self.table.getAccurateRipURL(),
|
||||
"http://www.accuraterip.com/accuraterip/a/5/d/"
|
||||
"dBAR-012-0013bd5a-00b8d489-c60af50d.bin")
|
||||
"http://www.accuraterip.com/accuraterip/a/5/d/"
|
||||
"dBAR-012-0013bd5a-00b8d489-c60af50d.bin")
|
||||
|
||||
def testDuration(self):
|
||||
self.assertEquals(self.table.duration(), 2761413)
|
||||
@@ -95,7 +95,7 @@ class MusicBrainzTestCase(tcommon.TestCase):
|
||||
|
||||
def testMusicBrainz(self):
|
||||
self.assertEquals(self.table.getMusicBrainzDiscId(),
|
||||
'49HHV7Eb8UKF3aQiNmu1GR8vKTY-')
|
||||
'49HHV7Eb8UKF3aQiNmu1GR8vKTY-')
|
||||
|
||||
|
||||
class PregapTestCase(tcommon.TestCase):
|
||||
|
||||
@@ -14,8 +14,7 @@ from whipper.test import common
|
||||
class CureTestCase(common.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.path = os.path.join(os.path.dirname(__file__),
|
||||
u'cure.toc')
|
||||
self.path = os.path.join(os.path.dirname(__file__), u'cure.toc')
|
||||
self.toc = toc.TocFile(self.path)
|
||||
self.toc.parse()
|
||||
self.assertEquals(len(self.toc.table.tracks), 13)
|
||||
@@ -26,7 +25,7 @@ class CureTestCase(common.TestCase):
|
||||
# its length is all of track 1 from .toc, plus the INDEX 00 length
|
||||
# of track 2
|
||||
self.assertEquals(self.toc.getTrackLength(t),
|
||||
(((6 * 60) + 16) * 75 + 45) + ((1 * 75) + 4))
|
||||
(((6 * 60) + 16) * 75 + 45) + ((1 * 75) + 4))
|
||||
# last track has unknown length
|
||||
t = self.toc.table.tracks[-1]
|
||||
self.assertEquals(self.toc.getTrackLength(t), -1)
|
||||
@@ -91,8 +90,7 @@ class CureTestCase(common.TestCase):
|
||||
|
||||
# we verify it because it has failed in readdisc in the past
|
||||
self.assertEquals(self.toc.table.getAccurateRipURL(),
|
||||
'http://www.accuraterip.com/accuraterip/'
|
||||
'3/c/4/dBAR-013-0019d4c3-00fe8924-b90c650d.bin')
|
||||
'http://www.accuraterip.com/accuraterip/3/c/4/dBAR-013-0019d4c3-00fe8924-b90c650d.bin') # noqa: E501
|
||||
|
||||
def testGetRealPath(self):
|
||||
self.assertRaises(KeyError, self.toc.getRealPath, u'track01.wav')
|
||||
@@ -110,8 +108,7 @@ class CureTestCase(common.TestCase):
|
||||
class BlocTestCase(common.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.path = os.path.join(os.path.dirname(__file__),
|
||||
u'bloc.toc')
|
||||
self.path = os.path.join(os.path.dirname(__file__), u'bloc.toc')
|
||||
self.toc = toc.TocFile(self.path)
|
||||
self.toc.parse()
|
||||
self.assertEquals(len(self.toc.table.tracks), 13)
|
||||
@@ -168,8 +165,7 @@ class BlocTestCase(common.TestCase):
|
||||
def testAccurateRip(self):
|
||||
# we verify it because it has failed in readdisc in the past
|
||||
self.assertEquals(self.toc.table.getAccurateRipURL(),
|
||||
'http://www.accuraterip.com/accuraterip/'
|
||||
'e/d/2/dBAR-013-001af2de-0105994e-ad0be00d.bin')
|
||||
'http://www.accuraterip.com/accuraterip/e/d/2/dBAR-013-001af2de-0105994e-ad0be00d.bin') # noqa: E501
|
||||
|
||||
# The Breeders - Mountain Battles has CDText
|
||||
|
||||
@@ -177,8 +173,7 @@ class BlocTestCase(common.TestCase):
|
||||
class BreedersTestCase(common.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.path = os.path.join(os.path.dirname(__file__),
|
||||
u'breeders.toc')
|
||||
self.path = os.path.join(os.path.dirname(__file__), u'breeders.toc')
|
||||
self.toc = toc.TocFile(self.path)
|
||||
self.toc.parse()
|
||||
self.assertEquals(len(self.toc.table.tracks), 13)
|
||||
@@ -194,7 +189,6 @@ class BreedersTestCase(common.TestCase):
|
||||
self.assertEquals(cdt['TITLE'], 'OVERGLAZED')
|
||||
|
||||
def testConvertCue(self):
|
||||
# self.toc.table.absolutize()
|
||||
self.failUnless(self.toc.table.hasTOC())
|
||||
cue = self.toc.table.cue()
|
||||
ref = self.readCue('breeders.cue')
|
||||
@@ -206,8 +200,7 @@ class BreedersTestCase(common.TestCase):
|
||||
class LadyhawkeTestCase(common.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.path = os.path.join(os.path.dirname(__file__),
|
||||
u'ladyhawke.toc')
|
||||
self.path = os.path.join(os.path.dirname(__file__), u'ladyhawke.toc')
|
||||
self.toc = toc.TocFile(self.path)
|
||||
self.toc.parse()
|
||||
self.assertEquals(len(self.toc.table.tracks), 13)
|
||||
@@ -221,12 +214,9 @@ class LadyhawkeTestCase(common.TestCase):
|
||||
|
||||
def testMusicBrainz(self):
|
||||
self.assertEquals(self.toc.table.getMusicBrainzDiscId(),
|
||||
"KnpGsLhvH.lPrNc1PBL21lb9Bg4-")
|
||||
"KnpGsLhvH.lPrNc1PBL21lb9Bg4-")
|
||||
self.assertEquals(self.toc.table.getMusicBrainzSubmitURL(),
|
||||
"https://musicbrainz.org/cdtoc/attach?toc="
|
||||
"1+12+195856+150+15687+31841+51016+66616+81352+99559+"
|
||||
"116070+133243+149997+161710+177832&"
|
||||
"tracks=12&id=KnpGsLhvH.lPrNc1PBL21lb9Bg4-")
|
||||
"https://musicbrainz.org/cdtoc/attach?toc=1+12+195856+150+15687+31841+51016+66616+81352+99559+116070+133243+149997+161710+177832&tracks=12&id=KnpGsLhvH.lPrNc1PBL21lb9Bg4-") # noqa: E501
|
||||
|
||||
# FIXME: I don't trust this toc, but I can't find the CD anymore
|
||||
|
||||
@@ -247,13 +237,13 @@ class CapitalMergeTestCase(common.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.toc1 = toc.TocFile(os.path.join(os.path.dirname(__file__),
|
||||
u'capital.1.toc'))
|
||||
u'capital.1.toc'))
|
||||
self.toc1.parse()
|
||||
self.assertEquals(len(self.toc1.table.tracks), 11)
|
||||
self.failUnless(self.toc1.table.tracks[-1].audio)
|
||||
|
||||
self.toc2 = toc.TocFile(os.path.join(os.path.dirname(__file__),
|
||||
u'capital.2.toc'))
|
||||
u'capital.2.toc'))
|
||||
self.toc2.parse()
|
||||
self.assertEquals(len(self.toc2.table.tracks), 1)
|
||||
self.failIf(self.toc2.table.tracks[-1].audio)
|
||||
@@ -272,7 +262,7 @@ class CapitalMergeTestCase(common.TestCase):
|
||||
# 197850+24320+44855+64090+77885+88095+104020+118245+129255+141765+
|
||||
# 164487+181780&tracks=11&id=MAj3xXf6QMy7G.BIFOyHyq4MySE-
|
||||
self.assertEquals(self.table.getMusicBrainzDiscId(),
|
||||
"MAj3xXf6QMy7G.BIFOyHyq4MySE-")
|
||||
"MAj3xXf6QMy7G.BIFOyHyq4MySE-")
|
||||
|
||||
def testDuration(self):
|
||||
# this matches track 11 end sector - track 1 start sector on
|
||||
@@ -321,8 +311,7 @@ class UnicodeTestCase(common.TestCase, common.UnicodeTestMixin):
|
||||
class TOTBLTestCase(common.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.path = os.path.join(os.path.dirname(__file__),
|
||||
u'totbl.fast.toc')
|
||||
self.path = os.path.join(os.path.dirname(__file__), u'totbl.fast.toc')
|
||||
self.toc = toc.TocFile(self.path)
|
||||
self.toc.parse()
|
||||
self.assertEquals(len(self.toc.table.tracks), 11)
|
||||
@@ -338,7 +327,7 @@ class StrokesTestCase(common.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.path = os.path.join(os.path.dirname(__file__),
|
||||
u'strokes-someday.toc')
|
||||
u'strokes-someday.toc')
|
||||
self.toc = toc.TocFile(self.path)
|
||||
self.toc.parse()
|
||||
self.assertEquals(len(self.toc.table.tracks), 1)
|
||||
@@ -358,14 +347,16 @@ class StrokesTestCase(common.TestCase):
|
||||
self.assertEquals(i1.path, u'data.wav')
|
||||
|
||||
cue = self._filterCue(self.toc.table.cue())
|
||||
ref = self._filterCue(open(os.path.join(os.path.dirname(__file__),
|
||||
'strokes-someday.eac.cue')).read()).decode('utf-8')
|
||||
ref = self._filterCue(
|
||||
open(os.path.join(
|
||||
os.path.dirname(__file__),
|
||||
'strokes-someday.eac.cue')).read()).decode('utf-8')
|
||||
common.diffStrings(ref, cue)
|
||||
|
||||
def _filterCue(self, output):
|
||||
# helper to be able to compare our generated .cue with the
|
||||
# EAC-extracted one
|
||||
discard = [ 'TITLE', 'PERFORMER', 'FLAGS', 'REM' ]
|
||||
discard = ['TITLE', 'PERFORMER', 'FLAGS', 'REM']
|
||||
lines = output.split('\n')
|
||||
|
||||
res = []
|
||||
@@ -385,21 +376,16 @@ class StrokesTestCase(common.TestCase):
|
||||
return '\n'.join(res)
|
||||
|
||||
|
||||
|
||||
|
||||
# Surfer Rosa has
|
||||
# track 00 consisting of 32 frames of SILENCE
|
||||
# track 11 Vamos with an INDEX 02
|
||||
# compared to an EAC single .cue file, all our offsets are 32 frames off
|
||||
# because the toc uses silence for track 01 index 00 while EAC puts it in
|
||||
# Range.wav
|
||||
|
||||
|
||||
class SurferRosaTestCase(common.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.path = os.path.join(os.path.dirname(__file__),
|
||||
u'surferrosa.toc')
|
||||
self.path = os.path.join(os.path.dirname(__file__), u'surferrosa.toc')
|
||||
self.toc = toc.TocFile(self.path)
|
||||
self.toc.parse()
|
||||
self.assertEquals(len(self.toc.table.tracks), 21)
|
||||
|
||||
@@ -15,7 +15,7 @@ class ParseTestCase(common.TestCase):
|
||||
def setUp(self):
|
||||
# report from Afghan Whigs - Sweet Son Of A Bitch
|
||||
path = os.path.join(os.path.dirname(__file__),
|
||||
'cdparanoia.progress')
|
||||
'cdparanoia.progress')
|
||||
self._parser = cdparanoia.ProgressParser(start=45990, stop=47719)
|
||||
|
||||
self._handle = open(path)
|
||||
@@ -27,11 +27,12 @@ class ParseTestCase(common.TestCase):
|
||||
q = '%.01f %%' % (self._parser.getTrackQuality() * 100.0, )
|
||||
self.assertEquals(q, '99.6 %')
|
||||
|
||||
|
||||
class Parse1FrameTestCase(common.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
path = os.path.join(os.path.dirname(__file__),
|
||||
'cdparanoia.progress.strokes')
|
||||
'cdparanoia.progress.strokes')
|
||||
self._parser = cdparanoia.ProgressParser(start=0, stop=0)
|
||||
|
||||
self._handle = open(path)
|
||||
@@ -49,7 +50,7 @@ class ErrorTestCase(common.TestCase):
|
||||
def setUp(self):
|
||||
# report from a rip with offset -1164 causing scsi errors
|
||||
path = os.path.join(os.path.dirname(__file__),
|
||||
'cdparanoia.progress.error')
|
||||
'cdparanoia.progress.error')
|
||||
self._parser = cdparanoia.ProgressParser(start=0, stop=10800)
|
||||
|
||||
self._handle = open(path)
|
||||
@@ -87,7 +88,7 @@ class CacheTestCase(common.TestCase):
|
||||
self.runner = task.SyncRunner(verbose=False)
|
||||
|
||||
path = os.path.join(os.path.dirname(__file__),
|
||||
'cdparanoia', 'PX-L890SA.cdparanoia-A.stderr')
|
||||
'cdparanoia', 'PX-L890SA.cdparanoia-A.stderr')
|
||||
t = AnalyzeFileTask(path)
|
||||
self.runner.run(t)
|
||||
self.failUnless(t.defeatsCache)
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
# -*- Mode: Python; test-case-name: whipper.test.test_program_cdparanoia -*-
|
||||
# vi:si:et:sw=4:sts=4:ts=4
|
||||
|
||||
import os
|
||||
|
||||
from whipper.program import cdrdao
|
||||
|
||||
from whipper.test import common
|
||||
|
||||
# TODO: Current test architecture makes testing cdrdao difficult. Revisit.
|
||||
|
||||
|
||||
class VersionTestCase(common.TestCase):
|
||||
def testGetVersion(self):
|
||||
v = cdrdao.getCDRDAOVersion()
|
||||
|
||||
@@ -5,6 +5,7 @@ import os
|
||||
from whipper.program import sox
|
||||
from whipper.test import common
|
||||
|
||||
|
||||
class PeakLevelTestCase(common.TestCase):
|
||||
def setUp(self):
|
||||
self.path = os.path.join(os.path.dirname(__file__), 'track.flac')
|
||||
|
||||
@@ -11,6 +11,7 @@ from whipper.test import common as tcommon
|
||||
base_track_file = os.path.join(os.path.dirname(__file__), u'track.flac')
|
||||
base_track_length = 10 * common.SAMPLES_PER_FRAME
|
||||
|
||||
|
||||
class AudioLengthTestCase(tcommon.TestCase):
|
||||
|
||||
def testLength(self):
|
||||
@@ -34,6 +35,7 @@ class AudioLengthPathTestCase(tcommon.TestCase):
|
||||
self.assertEquals(t.length, base_track_length)
|
||||
os.unlink(path)
|
||||
|
||||
|
||||
class NormalAudioLengthPathTestCase(AudioLengthPathTestCase):
|
||||
|
||||
def testSingleQuote(self):
|
||||
@@ -46,12 +48,13 @@ class NormalAudioLengthPathTestCase(AudioLengthPathTestCase):
|
||||
|
||||
|
||||
class UnicodeAudioLengthPathTestCase(AudioLengthPathTestCase,
|
||||
tcommon.UnicodeTestMixin):
|
||||
tcommon.UnicodeTestMixin):
|
||||
|
||||
def testUnicodePath(self):
|
||||
# this test makes sure we can checksum a unicode path
|
||||
self._testSuffix(u'whipper.test.B\xeate Noire.empty.flac')
|
||||
|
||||
|
||||
class AbsentFileAudioLengthPathTestCase(AudioLengthPathTestCase):
|
||||
def testAbsentFile(self):
|
||||
tempdir = tempfile.mkdtemp()
|
||||
@@ -60,6 +63,6 @@ class AbsentFileAudioLengthPathTestCase(AudioLengthPathTestCase):
|
||||
t = AudioLengthTask(path)
|
||||
runner = task.SyncRunner()
|
||||
self.assertRaises(task.TaskException, runner.run,
|
||||
t, verbose=False)
|
||||
t, verbose=False)
|
||||
|
||||
os.rmdir(tempdir)
|
||||
|
||||
Reference in New Issue
Block a user