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:
JoeLametta
2017-05-31 23:09:36 +02:00
committed by GitHub
parent b331f53b47
commit b6fb7e8a86
49 changed files with 614 additions and 539 deletions

View File

@@ -14,7 +14,7 @@ doc = handle.read()
soup = BeautifulSoup.BeautifulSoup(doc) soup = BeautifulSoup.BeautifulSoup(doc)
offsets = {} # offset -> total count offsets = {} # offset -> total count
rows = soup.findAll('tr') rows = soup.findAll('tr')
for row in rows: for row in rows:

View File

@@ -26,6 +26,7 @@ from whipper.common import accurip
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class Show(BaseCommand): class Show(BaseCommand):
summary = "show accuraterip data" summary = "show accuraterip data"
description = """ description = """
@@ -52,7 +53,6 @@ retrieves and display accuraterip data from the given URL
"Warning: response %d has %d tracks instead of %d\n" % ( "Warning: response %d has %d tracks instead of %d\n" % (
i, r.trackCount, count)) i, r.trackCount, count))
# checksum and confidence by track # checksum and confidence by track
for track in range(count): for track in range(count):
sys.stdout.write("Track %d:\n" % (track + 1)) sys.stdout.write("Track %d:\n" % (track + 1))

View File

@@ -24,6 +24,7 @@ logger = logging.getLogger(__name__)
# A: The prefix matching prevents passing '-h' (and possibly other # A: The prefix matching prevents passing '-h' (and possibly other
# options) to the child command. # options) to the child command.
class BaseCommand(): class BaseCommand():
""" """
A base command class for whipper commands. A base command class for whipper commands.

View File

@@ -24,10 +24,8 @@ import glob
import urllib2 import urllib2
import socket import socket
import sys import sys
import logging
import gobject import gobject
gobject.threads_init()
from whipper.command.basecommand import BaseCommand from whipper.command.basecommand import BaseCommand
from whipper.common import ( from whipper.common import (
accurip, common, config, drive, program, task accurip, common, config, drive, program, task
@@ -35,7 +33,8 @@ from whipper.common import (
from whipper.program import cdrdao, cdparanoia, utils from whipper.program import cdrdao, cdparanoia, utils
from whipper.result import result from whipper.result import result
import logging gobject.threads_init()
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -81,24 +80,26 @@ class _CD(BaseCommand):
def add_arguments(parser): def add_arguments(parser):
# FIXME: have a cache of these pickles somewhere # FIXME: have a cache of these pickles somewhere
parser.add_argument('-T', '--toc-pickle', parser.add_argument('-T', '--toc-pickle',
action="store", dest="toc_pickle", action="store", dest="toc_pickle",
help="pickle to use for reading and writing the TOC") help="pickle to use for reading and "
"writing the TOC")
parser.add_argument('-R', '--release-id', parser.add_argument('-R', '--release-id',
action="store", dest="release_id", action="store", dest="release_id",
help="MusicBrainz release id to match to (if there are multiple)") help="MusicBrainz release id to match to "
"(if there are multiple)")
parser.add_argument('-p', '--prompt', parser.add_argument('-p', '--prompt',
action="store_true", dest="prompt", action="store_true", dest="prompt",
help="Prompt if there are multiple matching releases") help="Prompt if there are multiple "
"matching releases")
parser.add_argument('-c', '--country', parser.add_argument('-c', '--country',
action="store", dest="country", action="store", dest="country",
help="Filter releases by country") help="Filter releases by country")
def do(self): def do(self):
self.config = config.Config() self.config = config.Config()
self.program = program.Program(self.config, self.program = program.Program(self.config,
record=self.options.record, record=self.options.record,
stdout=sys.stdout) stdout=sys.stdout)
self.runner = task.SyncRunner() self.runner = task.SyncRunner()
# if the device is mounted (data session), unmount it # if the device is mounted (data session), unmount it
@@ -110,8 +111,8 @@ class _CD(BaseCommand):
# first, read the normal TOC, which is fast # first, read the normal TOC, which is fast
self.ittoc = self.program.getFastToc(self.runner, self.ittoc = self.program.getFastToc(self.runner,
self.options.toc_pickle, self.options.toc_pickle,
self.device) self.device)
# already show us some info based on this # already show us some info based on this
self.program.getRipResult(self.ittoc.getCDDBDiscId()) 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 disc id %s\n" % self.mbdiscid)
sys.stdout.write("MusicBrainz lookup URL %s\n" % sys.stdout.write("MusicBrainz lookup URL %s\n" %
self.ittoc.getMusicBrainzSubmitURL()) self.ittoc.getMusicBrainzSubmitURL())
self.program.metadata = self.program.getMusicBrainz(self.ittoc, self.program.metadata = (
self.mbdiscid, self.program.getMusicBrainz(self.ittoc, self.mbdiscid,
release=self.options.release_id, release=self.options.release_id,
country=self.options.country, country=self.options.country,
prompt=self.options.prompt) prompt=self.options.prompt)
)
if not self.program.metadata: if not self.program.metadata:
# fall back to FreeDB for lookup # fall back to FreeDB for lookup
@@ -153,8 +155,9 @@ class _CD(BaseCommand):
# now, read the complete index table, which is slower # now, read the complete index table, which is slower
self.itable = self.program.getTable(self.runner, self.itable = self.program.getTable(self.runner,
self.ittoc.getCDDBDiscId(), self.ittoc.getCDDBDiscId(),
self.ittoc.getMusicBrainzDiscId(), self.device, offset) self.ittoc.getMusicBrainzDiscId(),
self.device, offset)
assert self.itable.getCDDBDiscId() == self.ittoc.getCDDBDiscId(), \ assert self.itable.getCDDBDiscId() == self.ittoc.getCDDBDiscId(), \
"full table's id %s differs from toc id %s" % ( "full table's id %s differs from toc id %s" % (
@@ -220,6 +223,7 @@ class Info(_CD):
def add_arguments(self): def add_arguments(self):
_CD.add_arguments(self.parser) _CD.add_arguments(self.parser)
class Rip(_CD): class Rip(_CD):
summary = "rip CD" summary = "rip CD"
# see whipper.common.program.Program.getPath for expansion # 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: try:
default_offset = config.Config().getReadOffset(*info) default_offset = config.Config().getReadOffset(*info)
sys.stdout.write("Using configured read offset %d\n" % sys.stdout.write("Using configured read offset %d\n" %
default_offset) default_offset)
except KeyError: except KeyError:
pass pass
_CD.add_arguments(self.parser) _CD.add_arguments(self.parser)
self.parser.add_argument('-L', '--logger', self.parser.add_argument('-L', '--logger',
action="store", dest="logger", default='whipper', action="store", dest="logger",
help="logger to use (choose from '" + "', '".join(loggers) + "')") default='whipper',
help="logger to use (choose from '"
"', '".join(loggers) + "')")
# FIXME: get from config # FIXME: get from config
self.parser.add_argument('-o', '--offset', self.parser.add_argument('-o', '--offset',
action="store", dest="offset", default=default_offset, action="store", dest="offset",
help="sample read offset") default=default_offset,
help="sample read offset")
self.parser.add_argument('-x', '--force-overread', self.parser.add_argument('-x', '--force-overread',
action="store_true", dest="overread", default=False, action="store_true", dest="overread",
help="Force overreading into the lead-out portion of the disc. " default=False,
"Works only if the patched cdparanoia package is installed " help="Force overreading into the "
"and the drive supports this feature. ") "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', self.parser.add_argument('-O', '--output-directory',
action="store", dest="output_directory", action="store", dest="output_directory",
default=os.path.relpath(os.getcwd()), default=os.path.relpath(os.getcwd()),
help="output directory; will be included in file paths in log") help="output directory; will be included "
"in file paths in log")
self.parser.add_argument('-W', '--working-directory', self.parser.add_argument('-W', '--working-directory',
action="store", dest="working_directory", action="store", dest="working_directory",
help="working directory; whipper will change to this directory " help="working directory; whipper will "
"and files will be created relative to it when not absolute") "change to this directory "
"and files will be created relative to "
"it when not absolute")
self.parser.add_argument('--track-template', self.parser.add_argument('--track-template',
action="store", dest="track_template", action="store", dest="track_template",
default=DEFAULT_TRACK_TEMPLATE, default=DEFAULT_TRACK_TEMPLATE,
help="template for track file naming (default default)") help="template for track file naming "
"(default default)")
self.parser.add_argument('--disc-template', self.parser.add_argument('--disc-template',
action="store", dest="disc_template", action="store", dest="disc_template",
default=DEFAULT_DISC_TEMPLATE, default=DEFAULT_DISC_TEMPLATE,
help="template for disc file naming (default default)") help="template for disc file naming "
"(default default)")
self.parser.add_argument('-U', '--unknown', self.parser.add_argument('-U', '--unknown',
action="store_true", dest="unknown", action="store_true", dest="unknown",
help="whether to continue ripping if the CD is unknown", help="whether to continue ripping if "
default=False) "the CD is unknown", default=False)
def handle_arguments(self): 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') self.options.disc_template = self.options.disc_template.decode('utf-8')
if self.options.offset is None: 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 " "also be specified at runtime using the "
"'--offset=value' argument") "'--offset=value' argument")
if self.options.working_directory is not None: 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: if self.options.logger:
try: try:
@@ -312,7 +329,6 @@ Log files will log the path to tracks relative to this directory.
logger.critical(msg) logger.critical(msg)
raise ValueError(msg) raise ValueError(msg)
def doCommand(self): def doCommand(self):
self.program.setWorkingDirectory(self.options.working_directory) self.program.setWorkingDirectory(self.options.working_directory)
self.program.outdir = self.options.output_directory.decode('utf-8') 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.overread = self.options.overread
self.program.result.logger = self.options.logger self.program.result.logger = self.options.logger
### write disc files # write disc files
disambiguate = False disambiguate = False
while True: while True:
discName = self.program.getPath(self.program.outdir, discName = self.program.getPath(self.program.outdir,
self.options.disc_template, self.mbdiscid, 0, self.options.disc_template,
disambiguate=disambiguate) self.mbdiscid, 0,
disambiguate=disambiguate)
dirname = os.path.dirname(discName) dirname = os.path.dirname(discName)
if os.path.exists(dirname): if os.path.exists(dirname):
sys.stdout.write("Output directory %s already exists\n" % 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')) logs = glob.glob(os.path.join(dirname, '*.log'))
if logs: if logs:
sys.stdout.write( sys.stdout.write(
@@ -344,14 +361,13 @@ Log files will log the path to tracks relative to this directory.
else: else:
sys.stdout.write("Creating output directory %s\n" % sys.stdout.write("Creating output directory %s\n" %
dirname.encode('utf-8')) dirname.encode('utf-8'))
os.makedirs(dirname) os.makedirs(dirname)
break break
# FIXME: say when we're continuing a rip # FIXME: say when we're continuing a rip
# FIXME: disambiguate if the pre-existing rip is different # FIXME: disambiguate if the pre-existing rip is different
# FIXME: turn this into a method # FIXME: turn this into a method
def ripIfNotRipped(number): 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) self.program.result.tracks.append(trackResult)
else: else:
logger.debug('ripIfNotRipped have trackresult, path %r' % logger.debug('ripIfNotRipped have trackresult, path %r' %
trackResult.filename) trackResult.filename)
path = self.program.getPath(self.program.outdir, path = self.program.getPath(self.program.outdir,
self.options.track_template, self.options.track_template,
self.mbdiscid, number, self.mbdiscid, number,
disambiguate=disambiguate) \ disambiguate=disambiguate) \
+ '.' + 'flac' + '.' + 'flac'
logger.debug('ripIfNotRipped: path %r' % path) logger.debug('ripIfNotRipped: path %r' % path)
trackResult.number = number trackResult.number = number
@@ -378,7 +394,9 @@ Log files will log the path to tracks relative to this directory.
if number > 0: if number > 0:
trackResult.pregap = self.itable.tracks[number - 1].getPregap() 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 # FIXME: optionally allow overriding reripping
if os.path.exists(path): 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'))) os.path.basename(path).encode('utf-8')))
try: try:
logger.debug('ripIfNotRipped: track %d, try %d', logger.debug('ripIfNotRipped: track %d, try %d',
number, tries) number, tries)
self.program.ripTrack(self.runner, trackResult, self.program.ripTrack(self.runner, trackResult,
offset=int(self.options.offset), offset=int(self.options.offset),
device=self.device, device=self.device,
taglist=self.program.getTagList(number), taglist=self.program.getTagList(
overread=self.options.overread, number),
what='track %d of %d%s' % ( overread=self.options.overread,
number, len(self.itable.tracks), extra)) what='track %d of %d%s' % (
number,
len(self.itable.tracks),
extra))
break break
except Exception, e: except Exception, e:
logger.debug('Got exception %r on try %d', logger.debug('Got exception %r on try %d',
e, tries) e, tries)
if tries == MAX_TRIES: if tries == MAX_TRIES:
logger.critical('Giving up on track %d after %d times' % ( 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'") "Rip attempts number is equal to 'MAX_TRIES'")
if trackResult.testcrc == trackResult.copycrc: if trackResult.testcrc == trackResult.copycrc:
sys.stdout.write('Checksums match for track %d\n' % sys.stdout.write('Checksums match for track %d\n' %
number) number)
else: else:
sys.stdout.write( sys.stdout.write(
'ERROR: checksums did not match for track %d\n' % 'ERROR: checksums did not match for track %d\n' %
number) number)
raise 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 # overlay this rip onto the Table
if number == 0: if number == 0:
# HTOA goes on index 0 of track 1 # HTOA goes on index 0 of track 1
# ignore silence in PREGAP # ignore silence in PREGAP
if trackResult.peak <= SILENT: 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.itable.setFile(1, 0, None,
self.ittoc.getTrackStart(1), number) self.ittoc.getTrackStart(1), number)
logger.debug('Unlinking %r', trackResult.filename) logger.debug('Unlinking %r', trackResult.filename)
os.unlink(trackResult.filename) os.unlink(trackResult.filename)
trackResult.filename = None trackResult.filename = None
sys.stdout.write('HTOA discarded, contains digital silence\n') sys.stdout.write(
'HTOA discarded, contains digital silence\n')
else: else:
self.itable.setFile(1, 0, trackResult.filename, self.itable.setFile(1, 0, trackResult.filename,
self.ittoc.getTrackStart(1), number) self.ittoc.getTrackStart(1), number)
else: else:
self.itable.setFile(number, 1, trackResult.filename, self.itable.setFile(number, 1, trackResult.filename,
self.ittoc.getTrackLength(number), number) self.ittoc.getTrackLength(number), number)
self.program.saveRipResult() self.program.saveRipResult()
# check for hidden track one audio # check for hidden track one audio
htoapath = None htoapath = None
htoa = self.program.getHTOA() htoa = self.program.getHTOA()
@@ -473,7 +497,7 @@ Log files will log the path to tracks relative to this directory.
start, stop = htoa start, stop = htoa
sys.stdout.write( sys.stdout.write(
'Found Hidden Track One Audio from frame %d to %d\n' % ( 'Found Hidden Track One Audio from frame %d to %d\n' % (
start, stop)) start, stop))
# rip it # rip it
ripIfNotRipped(0) ripIfNotRipped(0)
@@ -484,17 +508,18 @@ Log files will log the path to tracks relative to this directory.
if not track.audio: if not track.audio:
sys.stdout.write( sys.stdout.write(
'WARNING: skipping data track %d, not implemented\n' % ( 'WARNING: skipping data track %d, not implemented\n' % (
i + 1, )) i + 1, ))
# FIXME: make it work for now # FIXME: make it work for now
track.indexes[1].relative = 0 track.indexes[1].relative = 0
continue continue
ripIfNotRipped(i + 1) ripIfNotRipped(i + 1)
### write disc files # write disc files
discName = self.program.getPath(self.program.outdir, discName = self.program.getPath(self.program.outdir,
self.options.disc_template, self.mbdiscid, 0, self.options.disc_template,
disambiguate=disambiguate) self.mbdiscid, 0,
disambiguate=disambiguate)
dirname = os.path.dirname(discName) dirname = os.path.dirname(discName)
if not os.path.exists(dirname): if not os.path.exists(dirname):
os.makedirs(dirname) os.makedirs(dirname)
@@ -516,20 +541,22 @@ Log files will log the path to tracks relative to this directory.
u = '%s\n' % targetPath u = '%s\n' % targetPath
handle.write(u.encode('utf-8')) handle.write(u.encode('utf-8'))
if htoapath: if htoapath:
writeFile(handle, 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): for i, track in enumerate(self.itable.tracks):
if not track.audio: if not track.audio:
continue continue
path = self.program.getPath(self.program.outdir, path = self.program.getPath(self.program.outdir,
self.options.track_template, self.mbdiscid, i + 1, self.options.track_template,
disambiguate=disambiguate) + '.' + 'flac' self.mbdiscid, i + 1,
disambiguate=disambiguate
) + '.' + 'flac'
writeFile(handle, path, writeFile(handle, path,
self.itable.getTrackLength(i + 1) / common.FRAMES_PER_SECOND) (self.itable.getTrackLength(i + 1) /
common.FRAMES_PER_SECOND))
handle.close() handle.close()
@@ -556,14 +583,13 @@ Log files will log the path to tracks relative to this directory.
if responses: if responses:
sys.stdout.write('%d AccurateRip reponses found\n' % sys.stdout.write('%d AccurateRip reponses found\n' %
len(responses)) len(responses))
if responses[0].cddbDiscId != self.itable.getCDDBDiscId(): if responses[0].cddbDiscId != self.itable.getCDDBDiscId():
sys.stdout.write( sys.stdout.write(
"AccurateRip response discid different: %s\n" % "AccurateRip response discid different: %s\n" %
responses[0].cddbDiscId) responses[0].cddbDiscId)
self.program.verifyImage(self.runner, responses) self.program.verifyImage(self.runner, responses)
sys.stdout.write("\n".join( sys.stdout.write("\n".join(

View File

@@ -28,6 +28,7 @@ from whipper.result import result
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class RCCue(BaseCommand): class RCCue(BaseCommand):
summary = "write a cue file for the cached result" summary = "write a cue file for the cached result"
description = summary description = summary
@@ -148,9 +149,6 @@ class Encode(BaseCommand):
description = summary description = summary
def add_arguments(self): def add_arguments(self):
# here to avoid import gst eating our options
from whipper.common import encode
self.parser.add_argument('input', action='store', self.parser.add_argument('input', action='store',
help="audio file to encode") help="audio file to encode")
self.parser.add_argument('output', nargs='?', action='store', self.parser.add_argument('output', nargs='?', action='store',
@@ -174,8 +172,8 @@ class Encode(BaseCommand):
runner = task.SyncRunner() runner = task.SyncRunner()
logger.debug('Encoding %s to %s', logger.debug('Encoding %s to %s',
fromPath.encode('utf-8'), fromPath.encode('utf-8'),
toPath.encode('utf-8')) toPath.encode('utf-8'))
encodetask = encode.FlacEncodeTask(fromPath, toPath) encodetask = encode.FlacEncodeTask(fromPath, toPath)
runner.run(encodetask) runner.run(encodetask)
@@ -244,7 +242,7 @@ Example disc id: KnpGsLhvH.lPrNc1PBL21lb9Bg4-"""
sys.stdout.write('- Release %d:\n' % (i + 1, )) sys.stdout.write('- Release %d:\n' % (i + 1, ))
sys.stdout.write(' Artist: %s\n' % md.artist.encode('utf-8')) 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(' 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(' URL: %s\n' % md.url)
sys.stdout.write(' Tracks: %d\n' % len(md.tracks)) sys.stdout.write(' Tracks: %d\n' % len(md.tracks))
if md.catalogNumber: if md.catalogNumber:

View File

@@ -28,9 +28,10 @@ from whipper.program import cdparanoia
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class Analyze(BaseCommand): class Analyze(BaseCommand):
summary = "analyze caching behaviour of drive" 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 device_option = True
def do(self): def do(self):
@@ -51,14 +52,15 @@ class Analyze(BaseCommand):
info = drive.getDeviceInfo(self.options.device) info = drive.getDeviceInfo(self.options.device)
if not info: 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 return
sys.stdout.write( sys.stdout.write(
'Adding drive cache behaviour to configuration file.\n') 'Adding drive cache behaviour to configuration file.\n')
config.Config().setDefeatsCache(info[0], info[1], info[2], config.Config().setDefeatsCache(
t.defeatsCache) info[0], info[1], info[2], t.defeatsCache)
class List(BaseCommand): class List(BaseCommand):
@@ -77,7 +79,7 @@ class List(BaseCommand):
return return
try: try:
import cdio as _ import cdio as _ # noqa: F401 (TODO: fix it in a separate PR?)
except ImportError: except ImportError:
sys.stdout.write( sys.stdout.write(
'Install pycdio for vendor/model/release detection.\n') 'Install pycdio for vendor/model/release detection.\n')
@@ -87,7 +89,7 @@ class List(BaseCommand):
vendor, model, release = drive.getDeviceInfo(path) vendor, model, release = drive.getDeviceInfo(path)
sys.stdout.write( sys.stdout.write(
"drive: %s, vendor: %s, model: %s, release: %s\n" % ( "drive: %s, vendor: %s, model: %s, release: %s\n" % (
path, vendor, model, release)) path, vendor, model, release))
try: try:
offset = self.config.getReadOffset( offset = self.config.getReadOffset(
@@ -95,8 +97,10 @@ class List(BaseCommand):
sys.stdout.write( sys.stdout.write(
" Configured read offset: %d\n" % offset) " Configured read offset: %d\n" % offset)
except KeyError: except KeyError:
sys.stdout.write( # Note spaces at the beginning for pretty terminal output
" No read offset found. Run 'whipper offset find'\n") sys.stdout.write(" "
"No read offset found. "
"Run 'whipper offset find'\n")
try: try:
defeats = self.config.getDefeatsCache( defeats = self.config.getDefeatsCache(
@@ -108,7 +112,6 @@ class List(BaseCommand):
" Unknown whether audio cache can be defeated. " " Unknown whether audio cache can be defeated. "
"Run 'whipper drive analyze'\n") "Run 'whipper drive analyze'\n")
if not paths: if not paths:
sys.stdout.write('No drives found.\n') sys.stdout.write('No drives found.\n')

View File

@@ -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 disc id is %s\n' % mbdiscid)
sys.stdout.write("MusicBrainz lookup URL %s\n" % sys.stdout.write("MusicBrainz lookup URL %s\n" %
cueImage.table.getMusicBrainzSubmitURL()) cueImage.table.getMusicBrainzSubmitURL())
prog.metadata = prog.getMusicBrainz(cueImage.table, mbdiscid, prog.metadata = prog.getMusicBrainz(cueImage.table, mbdiscid,
release=self.options.release_id, release=self.options.release_id, # noqa: E501
country=self.options.country, country=self.options.country,
prompt=self.options.prompt) prompt=self.options.prompt)
if not prog.metadata: if not prog.metadata:
print 'Not in MusicBrainz database, skipping' print 'Not in MusicBrainz database, skipping'

View File

@@ -17,6 +17,7 @@ from whipper.program.utils import eject_device
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def main(): def main():
# set user agent # set user agent
musicbrainzngs.set_useragent("whipper", whipper.__version__, musicbrainzngs.set_useragent("whipper", whipper.__version__,
@@ -47,7 +48,7 @@ def main():
if isinstance(e.exception, common.EmptyError): if isinstance(e.exception, common.EmptyError):
logger.debug("EmptyError: %r", str(e.exception)) 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 return 255
# in python3 we can instead do `raise e.exception` as that would show # in python3 we can instead do `raise e.exception` as that would show
@@ -56,6 +57,7 @@ def main():
return 255 return 255
return ret if ret else 0 return ret if ret else 0
class Whipper(BaseCommand): class Whipper(BaseCommand):
description = """whipper is a CD ripping utility focusing on accuracy over speed. 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): def add_arguments(self):
self.parser.add_argument('-R', '--record', self.parser.add_argument('-R', '--record',
action='store_true', dest='record', action='store_true', dest='record',
help="record API requests for playback") help="record API requests for playback")
self.parser.add_argument('-v', '--version', self.parser.add_argument('-v', '--version',
action="store_true", dest="version", action="store_true", dest="version",
help="show version information") help="show version information")
self.parser.add_argument('-h', '--help', self.parser.add_argument('-h', '--help',
action="store_true", dest="help", action="store_true", dest="help",
help="show this help message and exit") help="show this help message and exit")
self.parser.add_argument('-e', '--eject', self.parser.add_argument('-e', '--eject',
action="store", dest="eject", default="always", action="store", dest="eject",
choices=('never', 'failure', 'success', 'always'), default="always",
help="when to eject disc (default: always)") choices=('never', 'failure',
'success', 'always'),
help="when to eject disc (default: always)")
def handle_arguments(self): def handle_arguments(self):
if self.options.help: if self.options.help:

View File

@@ -22,19 +22,17 @@ import argparse
import os import os
import sys import sys
import tempfile import tempfile
import logging
import gobject import gobject
gobject.threads_init()
from whipper.command.basecommand import BaseCommand 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.common import task as ctask
from whipper.program import cdrdao, cdparanoia, utils from whipper.program import cdrdao, cdparanoia, utils
from whipper.common import checksum from whipper.common import checksum
from whipper.extern.task import task from whipper.extern.task import task
import logging gobject.threads_init()
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# see http://www.accuraterip.com/driveoffsets.htm # see http://www.accuraterip.com/driveoffsets.htm
@@ -117,7 +115,7 @@ CD in the AccurateRip database."""
if responses[0].cddbDiscId != table.getCDDBDiscId(): if responses[0].cddbDiscId != table.getCDDBDiscId():
logger.warning("AccurateRip response discid different: %s", logger.warning("AccurateRip response discid different: %s",
responses[0].cddbDiscId) responses[0].cddbDiscId)
# now rip the first track at various offsets, calculating AccurateRip # now rip the first track at various offsets, calculating AccurateRip
# CRC, and matching it against the retrieved ones # CRC, and matching it against the retrieved ones
@@ -137,7 +135,7 @@ CD in the AccurateRip database."""
# let MissingDependency fall through # let MissingDependency fall through
if isinstance(e.exception, if isinstance(e.exception,
common.MissingDependencyException): common.MissingDependencyException):
raise e raise e
if isinstance(e.exception, cdparanoia.FileSizeError): if isinstance(e.exception, cdparanoia.FileSizeError):
@@ -159,7 +157,7 @@ CD in the AccurateRip database."""
logger.debug('MATCHED against response %d' % i) logger.debug('MATCHED against response %d' % i)
sys.stdout.write( sys.stdout.write(
'Offset of device is likely %d, confirming ...\n' % 'Offset of device is likely %d, confirming ...\n' %
offset) offset)
# now try and rip all other tracks as well, except for the # now try and rip all other tracks as well, except for the
# last one (to avoid readers that can't do overread # last one (to avoid readers that can't do overread
@@ -185,7 +183,7 @@ CD in the AccurateRip database."""
else: else:
sys.stdout.write( sys.stdout.write(
'Only %d of %d tracks matched, continuing ...\n' % ( '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('No matching offset found.\n')
sys.stdout.write('Consider trying again with a different disc.\n') sys.stdout.write('Consider trying again with a different disc.\n')
@@ -201,16 +199,19 @@ CD in the AccurateRip database."""
os.close(fd) os.close(fd)
t = cdparanoia.ReadTrackTask(path, table, t = cdparanoia.ReadTrackTask(path, table,
table.getTrackStart(track), table.getTrackEnd(track), table.getTrackStart(
overread=False, offset=offset, device=self.options.device) track), table.getTrackEnd(track),
overread=False, offset=offset,
device=self.options.device)
t.description = 'Ripping track %d with read offset %d' % ( t.description = 'Ripping track %d with read offset %d' % (
track, offset) track, offset)
runner.run(t) runner.run(t)
# TODO MW: Update this to also use the v2 checksum(s) # TODO MW: Update this to also use the v2 checksum(s)
t = checksum.FastAccurateRipChecksumTask(path, trackNumber=track, t = checksum.FastAccurateRipChecksumTask(path,
trackCount=len(table.tracks), wave=True, v2=False) trackNumber=track,
trackCount=len(table.tracks),
wave=True, v2=False)
runner.run(t) runner.run(t)
os.unlink(path) os.unlink(path)
@@ -218,17 +219,19 @@ CD in the AccurateRip database."""
def _foundOffset(self, device, offset): def _foundOffset(self, device, offset):
sys.stdout.write('\nRead offset of device is: %d.\n' % sys.stdout.write('\nRead offset of device is: %d.\n' %
offset) offset)
info = drive.getDeviceInfo(device) info = drive.getDeviceInfo(device)
if not info: 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 return
sys.stdout.write('Adding read offset to configuration file.\n') sys.stdout.write('Adding read offset to configuration file.\n')
config.Config().setReadOffset(info[0], info[1], info[2], config.Config().setReadOffset(info[0], info[1], info[2],
offset) offset)
class Offset(BaseCommand): class Offset(BaseCommand):

View File

@@ -30,6 +30,7 @@ from whipper.common import directory
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class Persister: class Persister:
""" """
I wrap an optional pickle to persist an object to disk. I wrap an optional pickle to persist an object to disk.
@@ -125,7 +126,7 @@ class PersistedCache:
try: try:
os.makedirs(self.path) os.makedirs(self.path)
except OSError, e: except OSError, e:
if e.errno != 17: # FIXME if e.errno != 17: # FIXME
raise raise
def _getPath(self, key): def _getPath(self, key):
@@ -176,7 +177,7 @@ class ResultCache:
presult.persist(presult.object) presult.persist(presult.object)
else: else:
logger.debug('result for cddbdiscid %r found in cache, reusing', logger.debug('result for cddbdiscid %r found in cache, reusing',
cddbdiscid) cddbdiscid)
return presult return presult
@@ -218,7 +219,7 @@ class TableCache:
ptable.object = None ptable.object = None
else: else:
logger.debug('no valid cached table found for %r' % logger.debug('no valid cached table found for %r' %
cddbdiscid) cddbdiscid)
if not ptable.object: if not ptable.object:
# get an empty persistable from the writable location # get an empty persistable from the writable location

View File

@@ -67,8 +67,9 @@ class FastAccurateRipChecksumTask(etask.Task):
self.schedule(0.0, self._arc) self.schedule(0.0, self._arc)
def _arc(self): def _arc(self):
arc = accuraterip_checksum(self.path, self.trackNumber, self.trackCount, arc = accuraterip_checksum(self.path, self.trackNumber,
self._wave, self._v2) self.trackCount,
self._wave, self._v2)
self.checksum = arc self.checksum = arc
self.stop() self.stop()

View File

@@ -21,7 +21,6 @@
import os import os
import os.path import os.path
import commands
import math import math
import subprocess import subprocess
@@ -32,7 +31,7 @@ logger = logging.getLogger(__name__)
FRAMES_PER_SECOND = 75 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 WORDS_PER_FRAME = SAMPLES_PER_FRAME * 2
BYTES_PER_FRAME = SAMPLES_PER_FRAME * 4 BYTES_PER_FRAME = SAMPLES_PER_FRAME * 4
@@ -41,6 +40,7 @@ class EjectError(SystemError):
""" """
Possibly ejects the drive in command.main. Possibly ejects the drive in command.main.
""" """
def __init__(self, device, *args): def __init__(self, device, *args):
""" """
args is a tuple used by BaseException.__str__ args is a tuple used by BaseException.__str__
@@ -60,7 +60,7 @@ def msfToFrames(msf):
@rtype: int @rtype: int
@returns: number of frames @returns: number of frames
""" """
if not ':' in msf: if ':' not in msf:
return int(msf) return int(msf)
m, s, f = msf.split(':') m, s, f = msf.split(':')
@@ -133,6 +133,7 @@ def formatTime(seconds, fractional=3):
return " ".join(chunks) return " ".join(chunks)
class MissingDependencyException(Exception): class MissingDependencyException(Exception):
dependency = None dependency = None
@@ -144,6 +145,7 @@ class MissingDependencyException(Exception):
class EmptyError(Exception): class EmptyError(Exception):
pass pass
class MissingFrames(Exception): class MissingFrames(Exception):
""" """
Less frames decoded than expected. Less frames decoded than expected.
@@ -289,8 +291,8 @@ class VersionGetter(object):
try: try:
p = asyncsub.Popen(self._args, p = asyncsub.Popen(self._args,
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, close_fds=True) stderr=subprocess.PIPE, close_fds=True)
p.wait() p.wait()
output = asyncsub.recv_some(p, e=0, stderr=1) output = asyncsub.recv_some(p, e=0, stderr=1)
vre = self._regexp.search(output) vre = self._regexp.search(output)

View File

@@ -30,6 +30,7 @@ from whipper.common import directory
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class Config: class Config:
def __init__(self, path=None): def __init__(self, path=None):
@@ -46,7 +47,7 @@ class Config:
self._parser.readfp(f) self._parser.readfp(f)
logger.info('Loaded %d sections from config file' % logger.info('Loaded %d sections from config file' %
len(self._parser.sections())) len(self._parser.sections()))
def write(self): def write(self):
fd, path = tempfile.mkstemp(suffix=u'.whipperrc') fd, path = tempfile.mkstemp(suffix=u'.whipperrc')
@@ -55,8 +56,7 @@ class Config:
handle.close() handle.close()
shutil.move(path, self._path) shutil.move(path, self._path)
# any section
### any section
def _getter(self, suffix, section, option): def _getter(self, suffix, section, option):
methodName = 'get' + suffix methodName = 'get' + suffix
@@ -72,7 +72,7 @@ class Config:
def getboolean(self, section, option): def getboolean(self, section, option):
return self._getter('boolean', section, option) return self._getter('boolean', section, option)
### drive sections # drive sections
def setReadOffset(self, vendor, model, release, offset): def setReadOffset(self, vendor, model, release, offset):
""" """
@@ -96,7 +96,6 @@ class Config:
raise KeyError("Could not find read_offset for %s/%s/%s" % ( raise KeyError("Could not find read_offset for %s/%s/%s" % (
vendor, model, release)) vendor, model, release))
def setDefeatsCache(self, vendor, model, release, defeat): def setDefeatsCache(self, vendor, model, release, defeat):
""" """
Set whether the drive defeats the cache. Set whether the drive defeats the cache.
@@ -139,7 +138,7 @@ class Config:
return name return name
raise KeyError("Could not find configuration section for %s/%s/%s" % ( raise KeyError("Could not find configuration section for %s/%s/%s" % (
vendor, model, release)) vendor, model, release))
def _findOrCreateDriveSection(self, vendor, model, release): def _findOrCreateDriveSection(self, vendor, model, release):
try: try:

View File

@@ -21,6 +21,7 @@
from os import getenv, makedirs from os import getenv, makedirs
from os.path import join, expanduser, exists from os.path import join, expanduser, exists
def config_path(): def config_path():
path = join(getenv('XDG_CONFIG_HOME') or join(expanduser('~'), u'.config'), path = join(getenv('XDG_CONFIG_HOME') or join(expanduser('~'), u'.config'),
u'whipper') u'whipper')
@@ -28,6 +29,7 @@ def config_path():
makedirs(path) makedirs(path)
return join(path, u'whipper.conf') return join(path, u'whipper.conf')
def cache_path(name=None): def cache_path(name=None):
path = join(getenv('XDG_CACHE_HOME') or join(expanduser('~'), u'.cache'), path = join(getenv('XDG_CACHE_HOME') or join(expanduser('~'), u'.cache'),
u'whipper') u'whipper')
@@ -37,9 +39,10 @@ def cache_path(name=None):
makedirs(path) makedirs(path)
return path return path
def data_path(name=None): def data_path(name=None):
path = join(getenv('XDG_DATA_HOME') path = join(getenv('XDG_DATA_HOME') or
or join(expanduser('~'), u'.local/share'), join(expanduser('~'), u'.local/share'),
u'whipper') u'whipper')
if name: if name:
path = join(path, name) path = join(path, name)

View File

@@ -23,6 +23,7 @@ import os
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def _listify(listOrString): def _listify(listOrString):
if type(listOrString) == str: if type(listOrString) == str:
return [listOrString, ] return [listOrString, ]
@@ -64,7 +65,6 @@ def getDeviceInfo(path):
import cdio import cdio
except ImportError: except ImportError:
return None return None
device = cdio.Device(path) device = cdio.Device(path)
ok, vendor, model, release = device.get_hwinfo() ok, vendor, model, release = device.get_hwinfo()

View File

@@ -29,6 +29,7 @@ from whipper.program import flac
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class SoxPeakTask(task.Task): class SoxPeakTask(task.Task):
description = 'Calculating peak level' description = 'Calculating peak level'
@@ -44,6 +45,7 @@ class SoxPeakTask(task.Task):
self.peak = sox.peak_level(self.track_path) self.peak = sox.peak_level(self.track_path)
self.stop() self.stop()
class FlacEncodeTask(task.Task): class FlacEncodeTask(task.Task):
description = 'Encoding to FLAC' 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.new_path = flac.encode(self.track_path, self.track_out_path)
self.stop() 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): 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' description = 'Writing tags to FLAC'
def __init__(self, track_path, tags): def __init__(self, track_path, tags):

View File

@@ -28,7 +28,7 @@ import logging
logger = logging.getLogger(__name__) 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): class MusicBrainzException(Exception):
@@ -47,7 +47,7 @@ class NotFoundException(MusicBrainzException):
class TrackMetadata(object): class TrackMetadata(object):
artist = None artist = None
title = None title = None
duration = None # in ms duration = None # in ms
mbid = None mbid = None
sortName = None sortName = None
mbidArtist = None mbidArtist = None
@@ -131,7 +131,6 @@ class _Credit(list):
return "".join(res) return "".join(res)
def getSortName(self): def getSortName(self):
return self.joiner(lambda i: i.get('artist').get('sort-name', None)) return self.joiner(lambda i: i.get('artist').get('sort-name', None))
@@ -140,7 +139,7 @@ class _Credit(list):
def getIds(self): def getIds(self):
return self.joiner(lambda i: i.get('artist').get('id', None), return self.joiner(lambda i: i.get('artist').get('id', None),
joinString=";") joinString=";")
def _getMetadata(releaseShort, release, discid, country=None): def _getMetadata(releaseShort, release, discid, country=None):
@@ -152,7 +151,7 @@ def _getMetadata(releaseShort, release, discid, country=None):
@rtype: L{DiscMetadata} or None @rtype: L{DiscMetadata} or None
""" """
logger.debug('getMetadata for release id %r', logger.debug('getMetadata for release id %r',
release['id']) release['id'])
if not release['id']: if not release['id']:
logger.warning('No id for release %r', release) logger.warning('No id for release %r', release)
return None return None
@@ -173,7 +172,6 @@ def _getMetadata(releaseShort, release, discid, country=None):
if discCredit[0]['artist']['id'] == VA_ID: if discCredit[0]['artist']['id'] == VA_ID:
discMD.various = True discMD.various = True
if len(discCredit) > 1: if len(discCredit) > 1:
logger.debug('artist-credit more than 1: %r', discCredit) logger.debug('artist-credit more than 1: %r', discCredit)
@@ -233,8 +231,9 @@ def _getMetadata(releaseShort, release, discid, country=None):
# FIXME: unit of duration ? # FIXME: unit of duration ?
track.duration = int(t['recording'].get('length', 0)) track.duration = int(t['recording'].get('length', 0))
if not track.duration: if not track.duration:
logger.warning('track %r (%r) does not have duration' % ( logger.warning(
track.title, track.mbid)) 'track %r (%r) does not have duration' %
(track.title, track.mbid))
tainted = True tainted = True
else: else:
duration += track.duration duration += track.duration
@@ -270,8 +269,8 @@ def musicbrainz(discid, country=None, record=False):
ret = [] ret = []
try: try:
result = musicbrainzngs.get_releases_by_discid(discid, result = musicbrainzngs.get_releases_by_discid(
includes=["artists", "recordings", "release-groups"]) discid, includes=["artists", "recordings", "release-groups"])
except musicbrainzngs.ResponseError, e: except musicbrainzngs.ResponseError, e:
if isinstance(e.cause, urllib2.HTTPError): if isinstance(e.cause, urllib2.HTTPError):
if e.cause.code == 404: 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" # The result can either be a "disc" or a "cdstub"
if result.get('disc'): if result.get('disc'):
logger.debug('found %d releases for discid %r', 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) _record(record, 'releases', discid, result)
# Display the returned results to the user. # Display the returned results to the user.

View File

@@ -51,12 +51,13 @@ class PathFilter(object):
# change all fancy single/double quotes to normal quotes # change all fancy single/double quotes to normal quotes
if self._quotes: if self._quotes:
path = re.sub(ur'[\xc2\xb4\u2018\u2019\u201b]', "'", path, path = re.sub(ur'[\xc2\xb4\u2018\u2019\u201b]', "'", path,
re.UNICODE) re.UNICODE)
path = re.sub(ur'[\u201c\u201d\u201f]', '"', path, re.UNICODE) path = re.sub(ur'[\u201c\u201d\u201f]', '"', path, re.UNICODE)
if self._special: if self._special:
path = separators(path) path = separators(path)
path = re.sub(r'[\*\?&!\'\"\$\(\)`{}\[\]<>]', '_', path, re.UNICODE) path = re.sub(r'[\*\?&!\'\"\$\(\)`{}\[\]<>]',
'_', path, re.UNICODE)
if self._fat: if self._fat:
path = separators(path) path = separators(path)

View File

@@ -76,7 +76,7 @@ class Program:
'special': False 'special': False
}.items(): }.items():
value = None value = None
value = self._config.getboolean('main', 'path_filter_'+ key) value = self._config.getboolean('main', 'path_filter_' + key)
if value is None: if value is None:
value = default value = default
@@ -105,9 +105,8 @@ class Program:
version = cdrdao.getCDRDAOVersion() version = cdrdao.getCDRDAOVersion()
if V(version) < V('1.2.3rc2'): if V(version) < V('1.2.3rc2'):
sys.stdout.write('Warning: cdrdao older than 1.2.3 has a ' sys.stdout.write('Warning: cdrdao older than 1.2.3 has a '
'pre-gap length bug.\n' 'pre-gap length bug.\n'
'See http://sourceforge.net/tracker/?func=detail' 'See http://sourceforge.net/tracker/?func=detail&aid=604751&group_id=2171&atid=102171\n') # noqa: E501
'&aid=604751&group_id=2171&atid=102171\n')
t = cdrdao.ReadTOCTask(device) t = cdrdao.ReadTOCTask(device)
ptoc.persist(t.table) ptoc.persist(t.table)
toc = ptoc.object toc = ptoc.object
@@ -133,17 +132,17 @@ class Program:
itable = tdict[offset] itable = tdict[offset]
if not itable: if not itable:
logger.debug('getTable: cddbdiscid %s, mbdiscid %s not in cache for offset %s, ' logger.debug('getTable: cddbdiscid %s, mbdiscid %s not '
'reading table' % ( 'in cache for offset %s, reading table' % (
cddbdiscid, mbdiscid, offset)) cddbdiscid, mbdiscid, offset))
t = cdrdao.ReadTableTask(device) t = cdrdao.ReadTableTask(device)
itable = t.table itable = t.table
tdict[offset] = itable tdict[offset] = itable
ptable.persist(tdict) ptable.persist(tdict)
logger.debug('getTable: read table %r' % itable) logger.debug('getTable: read table %r' % itable)
else: else:
logger.debug('getTable: cddbdiscid %s, mbdiscid %s in cache for offset %s' % ( logger.debug('getTable: cddbdiscid %s, mbdiscid %s in cache '
cddbdiscid, mbdiscid, offset)) 'for offset %s' % (cddbdiscid, mbdiscid, offset))
logger.debug('getTable: loaded table %r' % itable) logger.debug('getTable: loaded table %r' % itable)
assert itable.hasTOC() assert itable.hasTOC()
@@ -151,7 +150,7 @@ class Program:
self.result.table = itable self.result.table = itable
logger.debug('getTable: returning table with mb id %s' % logger.debug('getTable: returning table with mb id %s' %
itable.getMusicBrainzDiscId()) itable.getMusicBrainzDiscId())
return itable return itable
def getRipResult(self, cddbdiscid): def getRipResult(self, cddbdiscid):
@@ -200,11 +199,11 @@ class Program:
# default values # default values
v['A'] = 'Unknown Artist' v['A'] = 'Unknown Artist'
v['d'] = mbdiscid # fallback for title v['d'] = mbdiscid # fallback for title
v['r'] = 'unknown' v['r'] = 'unknown'
v['R'] = 'Unknown' v['R'] = 'Unknown'
v['B'] = '' # barcode v['B'] = '' # barcode
v['C'] = '' # catalog number v['C'] = '' # catalog number
v['x'] = 'flac' v['x'] = 'flac'
v['X'] = v['x'].upper() v['X'] = v['x'].upper()
v['y'] = '0000' v['y'] = '0000'
@@ -215,7 +214,6 @@ class Program:
else: else:
v['n'] = 'Unknown Track %d' % i v['n'] = 'Unknown Track %d' % i
if self.metadata: if self.metadata:
release = self.metadata.release or '0000' release = self.metadata.release or '0000'
v['y'] = release[:4] v['y'] = release[:4]
@@ -229,10 +227,12 @@ class Program:
v['r'] = self.metadata.releaseType.lower() v['r'] = self.metadata.releaseType.lower()
if i > 0: if i > 0:
try: 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( v['s'] = self._filter.filter(
self.metadata.tracks[i - 1].sortName) 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: except IndexError, e:
print 'ERROR: no track %d found, %r' % (i, e) print 'ERROR: no track %d found, %r' % (i, e)
raise raise
@@ -255,8 +255,6 @@ class Program:
ret = os.path.join(outdir, template % v) ret = os.path.join(outdir, template % v)
return ret return ret
def getCDDB(self, cddbdiscid): def getCDDB(self, cddbdiscid):
@@ -282,7 +280,8 @@ class Program:
return None 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} @type ittoc: L{whipper.image.table.Table}
""" """
@@ -291,7 +290,7 @@ class Program:
common.formatTime(ittoc.duration() / 1000.0), common.formatTime(ittoc.duration() / 1000.0),
ittoc.getAudioTracks())) ittoc.getAudioTracks()))
logger.debug('MusicBrainz submit url: %r', logger.debug('MusicBrainz submit url: %r',
ittoc.getMusicBrainzSubmitURL()) ittoc.getMusicBrainzSubmitURL())
ret = None ret = None
metadatas = None metadatas = None
@@ -300,8 +299,8 @@ class Program:
for _ in range(0, 4): for _ in range(0, 4):
try: try:
metadatas = mbngs.musicbrainz(mbdiscid, metadatas = mbngs.musicbrainz(mbdiscid,
country=country, country=country,
record=self._record) record=self._record)
break break
except mbngs.NotFoundException, e: except mbngs.NotFoundException, e:
break break
@@ -326,21 +325,23 @@ class Program:
for metadata in metadatas: for metadata in metadatas:
self._stdout.write('\n') self._stdout.write('\n')
self._stdout.write('Artist : %s\n' % self._stdout.write('Artist : %s\n' %
metadata.artist.encode('utf-8')) metadata.artist.encode('utf-8'))
self._stdout.write('Title : %s\n' % self._stdout.write('Title : %s\n' %
metadata.title.encode('utf-8')) metadata.title.encode('utf-8'))
self._stdout.write('Duration: %s\n' % 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('URL : %s\n' % metadata.url)
self._stdout.write('Release : %s\n' % metadata.mbid) self._stdout.write('Release : %s\n' % metadata.mbid)
self._stdout.write('Type : %s\n' % metadata.releaseType) self._stdout.write('Type : %s\n' % metadata.releaseType)
if metadata.barcode: if metadata.barcode:
self._stdout.write("Barcode : %s\n" % metadata.barcode) self._stdout.write("Barcode : %s\n" % metadata.barcode)
if metadata.catalogNumber: 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()) delta = abs(metadata.duration - ittoc.duration())
if not delta in deltas: if delta not in deltas:
deltas[delta] = [] deltas[delta] = []
deltas[delta].append(metadata) deltas[delta].append(metadata)
@@ -352,7 +353,8 @@ class Program:
if prompt: if prompt:
guess = (deltas[lowest])[0].mbid 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: if not release:
release = guess release = guess
@@ -360,15 +362,15 @@ class Program:
if release: if release:
metadatas = [m for m in metadatas if m.url.endswith(release)] metadatas = [m for m in metadatas if m.url.endswith(release)]
logger.debug('Asked for release %r, only kept %r', logger.debug('Asked for release %r, only kept %r',
release, metadatas) release, metadatas)
if len(metadatas) == 1: if len(metadatas) == 1:
self._stdout.write('\n') self._stdout.write('\n')
self._stdout.write('Picked requested release id %s\n' % self._stdout.write('Picked requested release id %s\n' %
release) release)
self._stdout.write('Artist : %s\n' % self._stdout.write('Artist : %s\n' %
metadatas[0].artist.encode('utf-8')) metadatas[0].artist.encode('utf-8'))
self._stdout.write('Title : %s\n' % self._stdout.write('Title : %s\n' %
metadatas[0].title.encode('utf-8')) metadatas[0].title.encode('utf-8'))
elif not metadatas: elif not metadatas:
self._stdout.write( self._stdout.write(
"Requested release id '%s', " "Requested release id '%s', "
@@ -385,22 +387,23 @@ class Program:
for i, metadata in enumerate(metadatas): for i, metadata in enumerate(metadatas):
if not artist == metadata.artist: if not artist == metadata.artist:
logger.warning("artist 0: %r and artist %d: %r " logger.warning("artist 0: %r and artist %d: %r "
"are not the same" % ( "are not the same" % (
artist, i, metadata.artist)) artist, i, metadata.artist))
if not releaseTitle == metadata.releaseTitle: if not releaseTitle == metadata.releaseTitle:
logger.warning("title 0: %r and title %d: %r " logger.warning("title 0: %r and title %d: %r "
"are not the same" % ( "are not the same" % (
releaseTitle, i, metadata.releaseTitle)) releaseTitle, i,
metadata.releaseTitle))
if (not release and len(deltas.keys()) > 1): if (not release and len(deltas.keys()) > 1):
self._stdout.write('\n') self._stdout.write('\n')
self._stdout.write('Picked closest match in duration.\n') self._stdout.write('Picked closest match in duration.\n')
self._stdout.write('Others may be wrong in MusicBrainz, ' self._stdout.write('Others may be wrong in MusicBrainz, '
'please correct.\n') 'please correct.\n')
self._stdout.write('Artist : %s\n' % self._stdout.write('Artist : %s\n' %
artist.encode('utf-8')) artist.encode('utf-8'))
self._stdout.write('Title : %s\n' % 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. # Select one of the returned releases. We just pick the first one.
ret = metadatas[0] ret = metadatas[0]
@@ -503,12 +506,13 @@ class Program:
raise raise
ret = trackResult.testcrc == t.checksum 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) trackResult.testcrc, t.checksum, ret)
return ret return ret
def ripTrack(self, runner, trackResult, offset, device, taglist, 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 Ripping the track may change the track's filename as stored in
trackResult. trackResult.
@@ -527,14 +531,15 @@ class Program:
os.makedirs(dirname) os.makedirs(dirname)
if not what: if not what:
what='track %d' % (trackResult.number, ) what = 'track %d' % (trackResult.number, )
t = cdparanoia.ReadVerifyTrackTask(trackResult.filename, t = cdparanoia.ReadVerifyTrackTask(trackResult.filename,
self.result.table, start, stop, overread, self.result.table, start,
offset=offset, stop, overread,
device=device, offset=offset,
taglist=taglist, device=device,
what=what) taglist=taglist,
what=what)
runner.run(t) runner.run(t)
@@ -571,7 +576,7 @@ class Program:
""" """
logger.debug('verifying Image against %d AccurateRip responses', logger.debug('verifying Image against %d AccurateRip responses',
len(responses or [])) len(responses or []))
cueImage = image.Image(self.cuePath) cueImage = image.Image(self.cuePath)
verifytask = image.ImageVerifyTask(cueImage) verifytask = image.ImageVerifyTask(cueImage)
@@ -587,7 +592,6 @@ class Program:
trackResult = self.result.getTrackResult(i + 1) trackResult = self.result.getTrackResult(i + 1)
trackResult.ARCRC = csum trackResult.ARCRC = csum
if not responses: if not responses:
logger.warning('No AccurateRip responses, cannot verify.') logger.warning('No AccurateRip responses, cannot verify.')
return return
@@ -615,8 +619,8 @@ class Program:
trackResult.ARDBConfidence = confidence trackResult.ARDBConfidence = confidence
if not trackResult.accurip: if not trackResult.accurip:
logger.warning("Track %02d: not matched in AccurateRip database", logger.warning("Track %02d: not matched in "
i + 1) "AccurateRip database", i + 1)
# I have seen AccurateRip responses with 0 as confidence # I have seen AccurateRip responses with 0 as confidence
# for example, Best of Luke Haines, disc 1, track 1 # for example, Best of Luke Haines, disc 1, track 1
@@ -632,7 +636,7 @@ class Program:
trackResult.ARDBMaxConfidence = maxConfidence trackResult.ARDBMaxConfidence = maxConfidence
if not response: if not response:
logger.warning('Track %02d: none of the responses matched.', logger.warning('Track %02d: none of the responses matched.',
i + 1) i + 1)
trackResult.ARDBCRC = int( trackResult.ARDBCRC = int(
maxResponse.checksums[i], 16) maxResponse.checksums[i], 16)
else: else:
@@ -650,7 +654,7 @@ class Program:
status = 'rip NOT accurate' status = 'rip NOT accurate'
if trackResult.accurip: if trackResult.accurip:
status = 'rip accurate ' status = 'rip accurate '
c = "(not found) " c = "(not found) "
ar = ", DB [notfound]" ar = ", DB [notfound]"
@@ -668,7 +672,7 @@ class Program:
if trackResult.ARCRC is None: if trackResult.ARCRC is None:
assert trackResult.number == 0, \ assert trackResult.number == 0, \
'no trackResult.ARCRC on non-HTOA track %d' % \ 'no trackResult.ARCRC on non-HTOA track %d' % \
trackResult.number trackResult.number
res.append("Track 0: unknown (not tracked)") res.append("Track 0: unknown (not tracked)")
else: else:
res.append("Track %2d: %s %s [%08x]%s" % ( res.append("Track %2d: %s %s [%08x]%s" % (

View File

@@ -56,7 +56,6 @@ class Operator(object):
operation = cls.deserialize(data) operation = cls.deserialize(data)
self._todo.append(operation) self._todo.append(operation)
done = os.path.join(self._statePath, self._key + '.done') done = os.path.join(self._statePath, self._key + '.done')
if os.path.exists(done): if os.path.exists(done):
with open(done, 'r') as handle: with open(done, 'r') as handle:

View File

@@ -39,9 +39,11 @@ class PopenTask(task.Task):
try: try:
self._popen = asyncsub.Popen(self.command, self._popen = asyncsub.Popen(self.command,
bufsize=self.bufsize, bufsize=self.bufsize,
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stdin=subprocess.PIPE,
stderr=subprocess.PIPE, close_fds=True, cwd=self.cwd) stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
close_fds=True, cwd=self.cwd)
except OSError, e: except OSError, e:
import errno import errno
if e.errno == errno.ENOENT: if e.errno == errno.ENOENT:
@@ -50,7 +52,7 @@ class PopenTask(task.Task):
raise raise
logger.debug('Started %r with pid %d', self.command, logger.debug('Started %r with pid %d', self.command,
self._popen.pid) self._popen.pid)
self.schedule(1.0, self._read, runner) self.schedule(1.0, self._read, runner)
@@ -92,23 +94,23 @@ class PopenTask(task.Task):
self.stop() self.stop()
def _done(self): 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: if self._popen.returncode >= 0:
logger.debug('Return code was %d', self._popen.returncode) logger.debug('Return code was %d', self._popen.returncode)
else: else:
logger.debug('Terminated with signal %d', logger.debug('Terminated with signal %d',
-self._popen.returncode) -self._popen.returncode)
self.setProgress(1.0) self.setProgress(1.0)
if self._popen.returncode != 0: if self._popen.returncode != 0:
self.failed() self.failed()
else: else:
self.done() self.done()
self.stop() self.stop()
return return
def abort(self): def abort(self):
logger.debug('Aborting, sending SIGTERM to %d', self._popen.pid) logger.debug('Aborting, sending SIGTERM to %d', self._popen.pid)
@@ -139,7 +141,6 @@ class PopenTask(task.Task):
""" """
pass pass
def commandMissing(self): def commandMissing(self):
""" """
Called when the command is missing. Called when the command is missing.

View File

@@ -95,7 +95,7 @@ class Popen(subprocess.Popen):
try: try:
written = os.write(self.stdin.fileno(), input) written = os.write(self.stdin.fileno(), input)
except OSError, why: except OSError, why:
if why[0] == errno.EPIPE: #broken pipe if why[0] == errno.EPIPE: # broken pipe
return self._close('stdin') return self._close('stdin')
raise raise
@@ -108,7 +108,7 @@ class Popen(subprocess.Popen):
flags = fcntl.fcntl(conn, fcntl.F_GETFL) flags = fcntl.fcntl(conn, fcntl.F_GETFL)
if not conn.closed: 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: try:
if not select.select([conn], [], [], 0)[0]: if not select.select([conn], [], [], 0)[0]:
@@ -125,13 +125,14 @@ class Popen(subprocess.Popen):
if not conn.closed: if not conn.closed:
fcntl.fcntl(conn, fcntl.F_SETFL, flags) fcntl.fcntl(conn, fcntl.F_SETFL, flags)
message = "Other end disconnected!" message = "Other end disconnected!"
def recv_some(p, t=.1, e=1, tr=5, stderr=0): def recv_some(p, t=.1, e=1, tr=5, stderr=0):
if tr < 1: if tr < 1:
tr = 1 tr = 1
x = time.time()+t x = time.time() + t
y = [] y = []
r = '' r = ''
pr = p.recv pr = p.recv
@@ -147,7 +148,7 @@ def recv_some(p, t=.1, e=1, tr=5, stderr=0):
elif r: elif r:
y.append(r) y.append(r)
else: else:
time.sleep(max((x-time.time())/tr, 0)) time.sleep(max((x - time.time()) / tr, 0))
return ''.join(y) return ''.join(y)
@@ -158,6 +159,7 @@ def send_all(p, data):
raise Exception(message) raise Exception(message)
data = buffer(data, sent) data = buffer(data, sent)
if __name__ == '__main__': if __name__ == '__main__':
if sys.platform == 'win32': if sys.platform == 'win32':
shell, commands, tail = ('cmd', ('dir /w', 'echo HELLO WORLD'), '\r\n') shell, commands, tail = ('cmd', ('dir /w', 'echo HELLO WORLD'), '\r\n')

View File

@@ -22,12 +22,13 @@ import sys
import gobject import gobject
class TaskException(Exception): class TaskException(Exception):
""" """
I wrap an exception that happened during task execution. I wrap an exception that happened during task execution.
""" """
exception = None # original exception exception = None # original exception
def __init__(self, exception, message=None): def __init__(self, exception, message=None):
self.exception = exception self.exception = exception
@@ -35,6 +36,8 @@ class TaskException(Exception):
self.args = (exception, message, ) self.args = (exception, message, )
# lifted from flumotion log module # lifted from flumotion log module
def _getExceptionMessage(exception, frame=-1, filename=None): def _getExceptionMessage(exception, frame=-1, filename=None):
""" """
Return a short message based on an exception, useful for debugging. 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. I am a stub for a log interface.
""" """
### log stubs # log stubs
def log(self, message, *args): def log(self, message, *args):
pass pass
@@ -112,8 +115,7 @@ class Task(LogStub):
_listeners = None _listeners = None
# subclass methods
### subclass methods
def start(self, runner): def start(self, runner):
""" """
Start the task. Start the task.
@@ -149,18 +151,20 @@ class Task(LogStub):
self.running = False self.running = False
if not self.runner: if not self.runner:
print 'ERROR: stopping task which is already stopped' print 'ERROR: stopping task which is already stopped'
import traceback; traceback.print_stack() import traceback
traceback.print_stack()
self.runner = None self.runner = None
self.debug('reset runner to None') self.debug('reset runner to None')
self._notifyListeners('stopped') self._notifyListeners('stopped')
### base class methods # base class methods
def setProgress(self, value): def setProgress(self, value):
""" """
Notify about progress changes bigger than the increment. Notify about progress changes bigger than the increment.
Called by subclass implementations as the task progresses. 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.progress = value
self._notifyListeners('progressed', value) self._notifyListeners('progressed', value)
self.log('notifying progress: %r on %r', value, self.description) self.log('notifying progress: %r on %r', value, self.description)
@@ -186,8 +190,8 @@ class Task(LogStub):
# for now # for now
if str(exception): if str(exception):
msg = ": %s" % str(exception) msg = ": %s" % str(exception)
line = "exception %(exc)s at %(filename)s:%(line)s: %(func)s()%(msg)s" \ line = "exception %(exc)s at %(filename)s:%(line)s: "
% locals() "%(func)s()%(msg)s" % locals()
self.exception = exception self.exception = exception
self.exceptionMessage = line self.exceptionMessage = line
@@ -211,11 +215,11 @@ class Task(LogStub):
def schedule(self, delta, callable, *args, **kwargs): def schedule(self, delta, callable, *args, **kwargs):
if not self.runner: if not self.runner:
print "ERROR: scheduling on a task that's altready stopped" print "ERROR: scheduling on a task that's altready stopped"
import traceback; traceback.print_stack() import traceback
traceback.print_stack()
return return
self.runner.schedule(self, delta, callable, *args, **kwargs) self.runner.schedule(self, delta, callable, *args, **kwargs)
def addListener(self, listener): def addListener(self, listener):
""" """
Add a listener for task status changes. Add a listener for task status changes.
@@ -236,12 +240,14 @@ class Task(LogStub):
except Exception, e: except Exception, e:
self.setException(e) self.setException(e)
# FIXME: should this become a real interface, like in zope ? # FIXME: should this become a real interface, like in zope ?
class ITaskListener(object): class ITaskListener(object):
""" """
I am an interface for objects listening to tasks. I am an interface for objects listening to tasks.
""" """
### listener callbacks # listener callbacks
def progressed(self, task, value): def progressed(self, task, value):
""" """
Implement me to be informed about progress. 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 # this is a Dummy task that can be used to test if this works at all
class DummyTask(Task): class DummyTask(Task):
def start(self, runner): def start(self, runner):
@@ -286,6 +291,7 @@ class DummyTask(Task):
self.schedule(1.0, self._wind) self.schedule(1.0, self._wind)
class BaseMultiTask(Task, ITaskListener): class BaseMultiTask(Task, ITaskListener):
""" """
I perform multiple tasks. I perform multiple tasks.
@@ -336,20 +342,20 @@ class BaseMultiTask(Task, ITaskListener):
task = self.tasks[self._task] task = self.tasks[self._task]
self._task += 1 self._task += 1
self.debug('BaseMultiTask.next(): starting task %d of %d: %r', 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) ..." % ( self.setDescription("%s (%d of %d) ..." % (
task.description, self._task, len(self.tasks))) task.description, self._task, len(self.tasks)))
task.addListener(self) task.addListener(self)
task.start(self.runner) task.start(self.runner)
self.debug('BaseMultiTask.next(): started task %d of %d: %r', 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: except Exception, e:
self.setException(e) self.setException(e)
self.debug('Got exception during next: %r', self.exceptionMessage) self.debug('Got exception during next: %r', self.exceptionMessage)
self.stop() self.stop()
return return
### ITaskListener methods # ITaskListener methods
def started(self, task): def started(self, task):
pass pass
@@ -362,10 +368,10 @@ class BaseMultiTask(Task, ITaskListener):
They should fall through to chaining up if there is an exception. They should fall through to chaining up if there is an exception.
""" """
self.log('BaseMultiTask.stopped: task %r (%d of %d)', 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: if task.exception:
self.log('BaseMultiTask.stopped: exception %r', self.log('BaseMultiTask.stopped: exception %r',
task.exceptionMessage) task.exceptionMessage)
self.exception = task.exception self.exception = task.exception
self.exceptionMessage = task.exceptionMessage self.exceptionMessage = task.exceptionMessage
self.stop() self.stop()
@@ -395,10 +401,10 @@ class MultiSeparateTask(BaseMultiTask):
def next(self): def next(self):
self.debug('MultiSeparateTask.next()') self.debug('MultiSeparateTask.next()')
# start next task # start next task
self.progress = 0.0 # reset progress for each task self.progress = 0.0 # reset progress for each task
BaseMultiTask.next(self) BaseMultiTask.next(self)
### ITaskListener methods # ITaskListener methods
def progressed(self, task, value): def progressed(self, task, value):
self.setProgress(value) self.setProgress(value)
@@ -406,6 +412,7 @@ class MultiSeparateTask(BaseMultiTask):
self.setDescription("%s (%d of %d) ..." % ( self.setDescription("%s (%d of %d) ..." % (
description, self._task, len(self.tasks))) description, self._task, len(self.tasks)))
class MultiCombinedTask(BaseMultiTask): class MultiCombinedTask(BaseMultiTask):
""" """
I perform multiple tasks. I perform multiple tasks.
@@ -415,7 +422,7 @@ class MultiCombinedTask(BaseMultiTask):
description = 'Doing various tasks combined' description = 'Doing various tasks combined'
_stopped = 0 _stopped = 0
### ITaskListener methods # ITaskListener methods
def progressed(self, task, value): def progressed(self, task, value):
self.setProgress(float(self._stopped + value) / len(self.tasks)) self.setProgress(float(self._stopped + value) / len(self.tasks))
@@ -424,6 +431,7 @@ class MultiCombinedTask(BaseMultiTask):
self.setProgress(float(self._stopped) / len(self.tasks)) self.setProgress(float(self._stopped) / len(self.tasks))
BaseMultiTask.stopped(self, task) BaseMultiTask.stopped(self, task)
class TaskRunner(LogStub): class TaskRunner(LogStub):
""" """
I am a base class for task runners. I am a base class for task runners.
@@ -439,7 +447,7 @@ class TaskRunner(LogStub):
""" """
raise NotImplementedError raise NotImplementedError
### methods for tasks to call # methods for tasks to call
def schedule(self, delta, callable, *args, **kwargs): def schedule(self, delta, callable, *args, **kwargs):
""" """
Schedule a single future call. Schedule a single future call.
@@ -456,9 +464,10 @@ class SyncRunner(TaskRunner, ITaskListener):
""" """
I run the task synchronously in a gobject MainLoop. I run the task synchronously in a gobject MainLoop.
""" """
def __init__(self, verbose=True): def __init__(self, verbose=True):
self._verbose = verbose 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): def run(self, task, verbose=None, skip=False):
self.debug('run task %r', task) self.debug('run task %r', task)
@@ -500,26 +509,25 @@ class SyncRunner(TaskRunner, ITaskListener):
self.debug('exception during start: %r', task.exceptionMessage) self.debug('exception during start: %r', task.exceptionMessage)
self.stopped(task) self.stopped(task)
def schedule(self, task, delta, callable, *args, **kwargs): def schedule(self, task, delta, callable, *args, **kwargs):
def c(): def c():
try: try:
self.log('schedule: calling %r(*args=%r, **kwargs=%r)', self.log('schedule: calling %r(*args=%r, **kwargs=%r)',
callable, args, kwargs) callable, args, kwargs)
callable(*args, **kwargs) callable(*args, **kwargs)
return False return False
except Exception, e: except Exception, e:
self.debug('exception when calling scheduled callable %r', self.debug('exception when calling scheduled callable %r',
callable) callable)
task.setException(e) task.setException(e)
self.stopped(task) self.stopped(task)
raise raise
self.log('schedule: scheduling %r(*args=%r, **kwargs=%r)', self.log('schedule: scheduling %r(*args=%r, **kwargs=%r)',
callable, args, kwargs) callable, args, kwargs)
gobject.timeout_add(int(delta * 1000L), c) gobject.timeout_add(int(delta * 1000L), c)
### ITaskListener methods # ITaskListener methods
def progressed(self, task, value): def progressed(self, task, value):
if not self._verboseRun: if not self._verboseRun:
return return
@@ -558,6 +566,7 @@ class SyncRunner(TaskRunner, ITaskListener):
self._output('%s %3d %%' % ( self._output('%s %3d %%' % (
self._task.description, self._task.progress * 100.0)) self._task.description, self._task.progress * 100.0))
if __name__ == '__main__': if __name__ == '__main__':
task = DummyTask() task = DummyTask()
runner = SyncRunner() runner = SyncRunner()

View File

@@ -142,11 +142,12 @@ class CueFile(object):
+ minutes * common.FRAMES_PER_SECOND * 60 + minutes * common.FRAMES_PER_SECOND * 60
logger.debug('found index %d of track %r in %r:%d', 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 ? # FIXME: what do we do about File's FORMAT ?
currentTrack.index(indexNumber, currentTrack.index(indexNumber,
path=currentFile.path, relative=frameOffset, path=currentFile.path, relative=frameOffset,
counter=counter) counter=counter)
continue continue
def message(self, number, message): def message(self, number, message):
@@ -166,8 +167,8 @@ class CueFile(object):
# last track, so no length known # last track, so no length known
return -1 return -1
thisIndex = track.indexes[1] # FIXME: could be more thisIndex = track.indexes[1] # FIXME: could be more
nextIndex = self.table.tracks[i + 1].indexes[1] # FIXME: could be 0 nextIndex = self.table.tracks[i + 1].indexes[1] # FIXME: could be 0
c = thisIndex.counter c = thisIndex.counter
if c is not None and c == nextIndex.counter: if c is not None and c == nextIndex.counter:

View File

@@ -52,8 +52,8 @@ class Image(object):
self._path = path self._path = path
self.cue = cue.CueFile(path) self.cue = cue.CueFile(path)
self.cue.parse() self.cue.parse()
self._offsets = [] # 0 .. trackCount - 1 self._offsets = [] # 0 .. trackCount - 1
self._lengths = [] # 0 .. trackCount - 1 self._lengths = [] # 0 .. trackCount - 1
self.table = None self.table = None
@@ -98,8 +98,8 @@ class Image(object):
# FIXME: this probably only works for non-compliant .CUE files # FIXME: this probably only works for non-compliant .CUE files
# where pregap is put at end of previous file # where pregap is put at end of previous file
t.index(1, absolute=offset, t.index(1, absolute=offset,
path=self.cue.table.tracks[i].getIndex(1).path, path=self.cue.table.tracks[i].getIndex(1).path,
relative=0) relative=0)
offset += length offset += length
@@ -128,17 +128,19 @@ class AccurateRipChecksumTask(task.MultiSeparateTask):
index = track.indexes[1] index = track.indexes[1]
length = cue.getTrackLength(track) length = cue.getTrackLength(track)
if length < 0: if length < 0:
logger.debug('track %d has unknown length' % (trackIndex + 1, )) logger.debug('track %d has unknown length' %
(trackIndex + 1, ))
else: else:
logger.debug('track %d is %d samples long' % ( logger.debug('track %d is %d samples long' % (
trackIndex + 1, length)) trackIndex + 1, length))
path = image.getRealPath(index.path) path = image.getRealPath(index.path)
checksumTask = checksum.FastAccurateRipChecksumTask(
checksumTask = checksum.FastAccurateRipChecksumTask(path, path,
trackNumber=trackIndex + 1, trackCount=len(cue.table.tracks), trackNumber=trackIndex + 1,
wave=True, v2=False) trackCount=len(cue.table.tracks),
wave=True, v2=False)
self.addTask(checksumTask) self.addTask(checksumTask)
@@ -201,8 +203,9 @@ class ImageVerifyTask(task.MultiSeparateTask):
break break
if taskk.length is None: if taskk.length is None:
raise ValueError("Track length was not found; look for " raise ValueError("Track length was not found; "
"earlier errors in debug log (set RIP_DEBUG=4)") "look for earlier errors "
"in debug log (set RIP_DEBUG=4)")
index = track.indexes[1] index = track.indexes[1]
assert taskk.length % common.SAMPLES_PER_FRAME == 0 assert taskk.length % common.SAMPLES_PER_FRAME == 0
end = taskk.length / common.SAMPLES_PER_FRAME end = taskk.length / common.SAMPLES_PER_FRAME
@@ -234,8 +237,9 @@ class ImageEncodeTask(task.MultiSeparateTask):
root, ext = os.path.splitext(os.path.basename(path)) root, ext = os.path.splitext(os.path.basename(path))
outpath = os.path.join(outdir, root + '.' + 'flac') outpath = os.path.join(outdir, root + '.' + 'flac')
logger.debug('schedule encode to %r', outpath) logger.debug('schedule encode to %r', outpath)
taskk = encode.FlacEncodeTask(path, os.path.join(outdir, taskk = encode.FlacEncodeTask(path,
root + '.' + 'flac')) os.path.join(outdir,
root + '.' + 'flac'))
self.addTask(taskk) self.addTask(taskk)
try: try:

View File

@@ -169,9 +169,9 @@ class Table(object):
@type cdtext: dict of str -> str @type cdtext: dict of str -> str
""" """
tracks = None # list of Track tracks = None # list of Track
leadout = None # offset where the leadout starts leadout = None # offset where the leadout starts
catalog = None # catalog number; FIXME: is this UPC ? catalog = None # catalog number; FIXME: is this UPC ?
cdtext = None cdtext = None
mbdiscid = None mbdiscid = None
@@ -305,7 +305,7 @@ class Table(object):
# duration = durationFrames / common.FRAMES_PER_SECOND # duration = durationFrames / common.FRAMES_PER_SECOND
# assert t == duration, "%r != %r" % (t, duration) # 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) result.append(leadoutSeconds)
value = (n % 0xff) << 24 | t << 8 | len(self.tracks) 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 values: %r', result)
logger.debug('cddb disc id debug: %s', logger.debug('cddb disc id debug: %s',
" ".join(["%08x" % value, ] + debug)) " ".join(["%08x" % value, ] + debug))
return result return result
@@ -472,7 +472,6 @@ class Table(object):
except IndexError: except IndexError:
pass pass
logger.debug('MusicBrainz values: %r', result) logger.debug('MusicBrainz values: %r', result)
return result return result
@@ -545,8 +544,8 @@ class Table(object):
main = ['PERFORMER', 'TITLE'] main = ['PERFORMER', 'TITLE']
for key in CDTEXT_FIELDS: for key in CDTEXT_FIELDS:
if key not in main and key in self.cdtext: if key not in main and key in self.cdtext:
lines.append(" %s %s" % (key, self.cdtext[key])) lines.append(" %s %s" % (key, self.cdtext[key]))
assert self.hasTOC(), "Table does not represent a full CD TOC" assert self.hasTOC(), "Table does not represent a full CD TOC"
lines.append('REM DISCID %s' % self.getCDDBDiscId().upper()) lines.append('REM DISCID %s' % self.getCDDBDiscId().upper())
@@ -607,7 +606,7 @@ class Table(object):
logger.debug('counter %d, writeFile' % counter) logger.debug('counter %d, writeFile' % counter)
writeFile(index.path) writeFile(index.path)
logger.debug('setting counter to index.counter %r' % logger.debug('setting counter to index.counter %r' %
index.counter) index.counter)
counter = index.counter counter = index.counter
# any time we hit the first index, write a TRACK statement # any time we hit the first index, write a TRACK statement
@@ -636,23 +635,25 @@ class Table(object):
if not index00.path: if not index00.path:
length = indexOne.absolute - index00.absolute length = indexOne.absolute - index00.absolute
lines.append(" PREGAP %s" % lines.append(" PREGAP %s" %
common.framesToMSF(length)) common.framesToMSF(length))
continue continue
# handle any other INDEX 00 after its TRACK # handle any other INDEX 00 after its TRACK
lines.append(" INDEX %02d %s" % (0, lines.append(" INDEX "
common.framesToMSF(index00.relative))) "%02d %s" % (0, common.framesToMSF(
index00.relative)))
if number > 0: if number > 0:
# index 00 is output after TRACK up above # index 00 is output after TRACK up above
lines.append(" INDEX %02d %s" % (number, lines.append(" INDEX %02d %s" % (number,
common.framesToMSF(index.relative))) common.framesToMSF(
index.relative)))
lines.append("") lines.append("")
return "\n".join(lines) return "\n".join(lines)
### methods that modify the table # methods that modify the table
def clearFiles(self): def clearFiles(self):
""" """
@@ -689,13 +690,14 @@ class Table(object):
@type index: C{int} @type index: C{int}
""" """
logger.debug('setFile: track %d, index %d, path %r, ' 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] t = self.tracks[track - 1]
i = t.indexes[index] i = t.indexes[index]
start = i.absolute start = i.absolute
assert start is not None, "index %r is missing absolute offset" % i 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 # FIXME: check border conditions here, esp. wrt. toc's off-by-one bug
while i.absolute <= end: while i.absolute <= end:
@@ -703,8 +705,8 @@ class Table(object):
i.relative = i.absolute - start i.relative = i.absolute - start
i.counter = counter i.counter = counter
logger.debug('Setting path %r, relative %r on ' logger.debug('Setting path %r, relative %r on '
'track %d, index %d, counter %r', 'track %d, index %d, counter %r',
path, i.relative, track, index, counter) path, i.relative, track, index, counter)
try: try:
track, index = self.getNextTrackIndex(track, index) track, index = self.getNextTrackIndex(track, index)
t = self.tracks[track - 1] t = self.tracks[track - 1]
@@ -733,10 +735,11 @@ class Table(object):
logger.debug('Track %d, index %d has no counter', t, i) logger.debug('Track %d, index %d has no counter', t, i)
break break
if index.counter != counter: 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 break
logger.debug('Setting absolute offset %d on track %d, index %d', 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 is not None:
if index.absolute != index.relative: if index.absolute != index.relative:
msg = 'Track %d, index %d had absolute %d,' \ msg = 'Track %d, index %d had absolute %d,' \
@@ -769,15 +772,17 @@ class Table(object):
for i in t.indexes.values(): for i in t.indexes.values():
if i.absolute is not None: if i.absolute is not None:
i.absolute += self.leadout + gap i.absolute += self.leadout + gap
logger.debug('Fixing track %02d, index %02d, absolute %d' % ( logger.debug('Fixing track %02d, index %02d, '
t.number, i.number, i.absolute)) 'absolute %d' % (
t.number, i.number, i.absolute))
if i.counter is not None: if i.counter is not None:
i.counter += sourceCounter i.counter += sourceCounter
logger.debug('Fixing track %02d, index %02d, counter %d' % ( logger.debug('Fixing track %02d, index %02d, '
t.number, i.number, i.counter)) 'counter %d' % (
t.number, i.number, i.counter))
self.tracks.append(t) self.tracks.append(t)
self.leadout += other.leadout + gap # FIXME self.leadout += other.leadout + gap # FIXME
logger.debug('Fixing leadout, now %d', self.leadout) logger.debug('Fixing leadout, now %d', self.leadout)
def _getSessionGap(self, session): def _getSessionGap(self, session):
@@ -795,7 +800,7 @@ class Table(object):
gap = 6900 gap = 6900
return gap return gap
### lookups # lookups
def getNextTrackIndex(self, track, index): def getNextTrackIndex(self, track, index):
""" """
@@ -857,8 +862,8 @@ class Table(object):
for t in self.tracks: for t in self.tracks:
for i in t.indexes.values(): for i in t.indexes.values():
if i.relative is None: if i.relative is None:
logger.debug('Track %02d, Index %02d does not have relative', logger.debug('Track %02d, Index %02d does not '
t.number, i.number) 'have relative', t.number, i.number)
return False return False
return True return True

View File

@@ -109,8 +109,8 @@ class Sources:
@type counter: int @type counter: int
@param offset: the absolute disc offset where this source starts @param offset: the absolute disc offset where this source starts
""" """
logger.debug('Appending source, counter %d, abs offset %d, source %r' % ( logger.debug('Appending source, counter %d, abs offset %d, '
counter, offset, source)) 'source %r' % (counter, offset, source))
self._sources.append((counter, offset, source)) self._sources.append((counter, offset, source))
def get(self, offset): def get(self, offset):
@@ -158,33 +158,34 @@ class TocFile(object):
relative = absolute - counterStart relative = absolute - counterStart
currentTrack.index(i, path=s.path, currentTrack.index(i, path=s.path,
absolute=absolute, absolute=absolute,
relative=relative, relative=relative,
counter=c) counter=c)
logger.debug( logger.debug(
'[track %02d index %02d] trackOffset %r, added %r', '[track %02d index %02d] trackOffset %r, added %r',
currentTrack.number, i, trackOffset, currentTrack.number, i, trackOffset,
currentTrack.getIndex(i)) currentTrack.getIndex(i))
def parse(self): 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 currentFile = None
currentTrack = None currentTrack = None
state = 'HEADER' state = 'HEADER'
counter = 0 # counts sources for audio data; SILENCE/ZERO/FILE # counts sources for audio data; SILENCE/ZERO/FILE
counter = 0
trackNumber = 0 trackNumber = 0
indexNumber = 0 indexNumber = 0
absoluteOffset = 0 # running absolute offset of where each track starts # running absolute offset: where each track starts
relativeOffset = 0 # running relative offset, relative to counter src absoluteOffset = 0
currentLength = 0 # accrued during TRACK record parsing; # running relative offset, relative to counter src
# length of current track as parsed so far; relativeOffset = 0
# reset on each TRACK statement # currentLength is accrued during TRACK record parsing length
totalLength = 0 # accrued during TRACK record parsing, total disc # of current track as parsed so far reset on each TRACK statement
pregapLength = 0 # length of the pre-gap, current track in for loop 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 # 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 # 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) logger.debug('Found disc CD-Text %s: %r', key, value)
elif state == 'TRACK': elif state == 'TRACK':
if key != 'ISRC' or not currentTrack \ 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', logger.debug('Found track CD-Text %s: %r',
key, value) key, value)
currentTrack.cdtext[key] = value currentTrack.cdtext[key] = value
# look for header elements # look for header elements
@@ -246,7 +247,7 @@ class TocFile(object):
# FIXME: track mode # FIXME: track mode
logger.debug('found track %d, mode %s, at absoluteOffset %d', logger.debug('found track %d, mode %s, at absoluteOffset %d',
trackNumber, trackMode, absoluteOffset) trackNumber, trackMode, absoluteOffset)
# reset counters relative to a track # reset counters relative to a track
currentLength = 0 currentLength = 0
@@ -299,18 +300,18 @@ class TocFile(object):
start = m.group('start') start = m.group('start')
length = m.group('length') length = m.group('length')
logger.debug('FILE %s, start %r, length %r', logger.debug('FILE %s, start %r, length %r',
filePath, common.msfToFrames(start), filePath, common.msfToFrames(start),
common.msfToFrames(length)) common.msfToFrames(length))
if not currentFile or filePath != currentFile.path: if not currentFile or filePath != currentFile.path:
counter += 1 counter += 1
relativeOffset = 0 relativeOffset = 0
logger.debug('track %d, switched to new FILE, ' logger.debug('track %d, switched to new FILE, '
'increased counter to %d', 'increased counter to %d',
trackNumber, counter) trackNumber, counter)
currentFile = File(filePath, common.msfToFrames(start), currentFile = File(filePath, common.msfToFrames(start),
common.msfToFrames(length)) common.msfToFrames(length))
self._sources.append(counter, absoluteOffset + currentLength, self._sources.append(counter, absoluteOffset + currentLength,
currentFile) currentFile)
currentLength += common.msfToFrames(length) currentLength += common.msfToFrames(length)
# look for DATAFILE lines # look for DATAFILE lines
@@ -319,20 +320,19 @@ class TocFile(object):
filePath = m.group('name') filePath = m.group('name')
length = m.group('length') length = m.group('length')
logger.debug('FILE %s, length %r', logger.debug('FILE %s, length %r',
filePath, common.msfToFrames(length)) filePath, common.msfToFrames(length))
if not currentFile or filePath != currentFile.path: if not currentFile or filePath != currentFile.path:
counter += 1 counter += 1
relativeOffset = 0 relativeOffset = 0
logger.debug('track %d, switched to new FILE, ' logger.debug('track %d, switched to new FILE, '
'increased counter to %d', 'increased counter to %d',
trackNumber, counter) trackNumber, counter)
# FIXME: assume that a MODE2_FORM_MIX track always starts at 0 # FIXME: assume that a MODE2_FORM_MIX track always starts at 0
currentFile = File(filePath, 0, common.msfToFrames(length)) currentFile = File(filePath, 0, common.msfToFrames(length))
self._sources.append(counter, absoluteOffset + currentLength, self._sources.append(counter, absoluteOffset + currentLength,
currentFile) currentFile)
currentLength += common.msfToFrames(length) currentLength += common.msfToFrames(length)
# look for START lines # look for START lines
m = _START_RE.search(line) m = _START_RE.search(line)
if m: if m:
@@ -349,10 +349,10 @@ class TocFile(object):
relativeOffset = absoluteOffset - counterStart relativeOffset = absoluteOffset - counterStart
currentTrack.index(0, path=s and s.path or None, currentTrack.index(0, path=s and s.path or None,
absolute=absoluteOffset, absolute=absoluteOffset,
relative=relativeOffset, counter=c) relative=relativeOffset, counter=c)
logger.debug('[track %02d index 00] added %r', 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 # store the pregapLength to add it when we index 1 for this
# track on the next iteration # track on the next iteration
pregapLength = length pregapLength = length
@@ -398,8 +398,8 @@ class TocFile(object):
# last track, so no length known # last track, so no length known
return -1 return -1
thisIndex = track.indexes[1] # FIXME: could be more thisIndex = track.indexes[1] # FIXME: could be more
nextIndex = self.table.tracks[i + 1].indexes[1] # FIXME: could be 0 nextIndex = self.table.tracks[i + 1].indexes[1] # FIXME: could be 0
c = thisIndex.counter c = thisIndex.counter
if c is not None and c == nextIndex.counter: if c is not None and c == nextIndex.counter:

View File

@@ -1,4 +1,3 @@
from os.path import exists
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
import logging import logging
@@ -7,6 +6,7 @@ logger = logging.getLogger(__name__)
ARB = 'accuraterip-checksum' ARB = 'accuraterip-checksum'
FLAC = 'flac' FLAC = 'flac'
def accuraterip_checksum(f, track, tracks, wave=False, v2=False): def accuraterip_checksum(f, track, tracks, wave=False, v2=False):
v = '--accuraterip-v1' v = '--accuraterip-v1'
if v2: if v2:

View File

@@ -79,12 +79,12 @@ _ERROR_RE = re.compile("^scsi_read error:")
class ProgressParser: class ProgressParser:
read = 0 # last [read] frame read = 0 # last [read] frame
wrote = 0 # last [wrote] frame wrote = 0 # last [wrote] frame
errors = 0 # count of number of scsi errors errors = 0 # count of number of scsi errors
_nframes = None # number of frames read on each [read] _nframes = None # number of frames read on each [read]
_firstFrames = None # number of frames read on first [read] _firstFrames = None # number of frames read on first [read]
reads = 0 # total number of reads reads = 0 # total number of reads
def __init__(self, start, stop): def __init__(self, start, stop):
""" """
@@ -99,7 +99,7 @@ class ProgressParser:
# FIXME: privatize # FIXME: privatize
self.read = start self.read = start
self._reads = {} # read count for each sector self._reads = {} # read count for each sector
def parse(self, line): def parse(self, line):
""" """
@@ -121,8 +121,7 @@ class ProgressParser:
def _parse_read(self, wordOffset): def _parse_read(self, wordOffset):
if wordOffset % common.WORDS_PER_FRAME != 0: if wordOffset % common.WORDS_PER_FRAME != 0:
logger.debug( logger.debug('THOMAS: not a multiple of %d: %d' % (
'THOMAS: not a multiple of %d: %d' % (
common.WORDS_PER_FRAME, wordOffset)) common.WORDS_PER_FRAME, wordOffset))
return return
@@ -139,7 +138,7 @@ class ProgressParser:
logger.debug('set firstFrames to %r', self._firstFrames) logger.debug('set firstFrames to %r', self._firstFrames)
markStart = None 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 # verify it either read nframes more or went back for verify
if frameOffset > self.read: if frameOffset > self.read:
@@ -156,13 +155,13 @@ class ProgressParser:
# we could use firstFrames as an estimate on how many frames this # we could use firstFrames as an estimate on how many frames this
# read, but this lowers our track quality needlessly where # read, but this lowers our track quality needlessly where
# EAC still reports 100% track quality # EAC still reports 100% track quality
markStart = frameOffset # - self._firstFrames markStart = frameOffset # - self._firstFrames
markEnd = frameOffset markEnd = frameOffset
# FIXME: doing this is way too slow even for a testcase, so disable # FIXME: doing this is way too slow even for a testcase, so disable
if False: if False:
for frame in range(markStart, markEnd): 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] = 0
self._reads[frame] += 1 self._reads[frame] += 1
@@ -189,7 +188,7 @@ class ProgressParser:
Each frame gets read twice. Each frame gets read twice.
More than two reads for a frame reduce track quality. 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 reads = self.reads
logger.debug('getTrackQuality: frames %d, reads %d' % (frames, reads)) logger.debug('getTrackQuality: frames %d, reads %d' % (frames, reads))
@@ -209,14 +208,14 @@ class ReadTrackTask(task.Task):
""" """
description = "Reading track" description = "Reading track"
quality = None # set at end of reading quality = None # set at end of reading
speed = None 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, 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. Read the given track.
@@ -249,7 +248,7 @@ class ReadTrackTask(task.Task):
self._start_time = None self._start_time = None
self._overread = overread self._overread = overread
self._buffer = "" # accumulate characters self._buffer = "" # accumulate characters
self._errors = [] self._errors = []
self.description = "%s %s" % (action, what) self.description = "%s %s" % (action, what)
@@ -271,31 +270,33 @@ class ReadTrackTask(task.Task):
stopOffset = self._stop - self._table.getTrackStart(i + 1) stopOffset = self._stop - self._table.getTrackStart(i + 1)
logger.debug('Ripping from %d to %d (inclusive)', logger.debug('Ripping from %d to %d (inclusive)',
self._start, self._stop) self._start, self._stop)
logger.debug('Starting at track %d, offset %d', logger.debug('Starting at track %d, offset %d',
startTrack, startOffset) startTrack, startOffset)
logger.debug('Stopping at track %d, offset %d', logger.debug('Stopping at track %d, offset %d',
stopTrack, stopOffset) stopTrack, stopOffset)
bufsize = 1024 bufsize = 1024
if self._overread: if self._overread:
argv = ["cdparanoia", "--stderr-progress", argv = ["cdparanoia", "--stderr-progress",
"--sample-offset=%d" % self._offset, "--force-overread", ] "--sample-offset=%d" % self._offset, "--force-overread", ]
else: else:
argv = ["cdparanoia", "--stderr-progress", argv = ["cdparanoia", "--stderr-progress",
"--sample-offset=%d" % self._offset, ] "--sample-offset=%d" % self._offset, ]
if self._device: if self._device:
argv.extend(["--force-cdrom-device", self._device, ]) argv.extend(["--force-cdrom-device", self._device, ])
argv.extend(["%d[%s]-%d[%s]" % ( argv.extend(["%d[%s]-%d[%s]" % (
startTrack, common.framesToHMSF(startOffset), startTrack, common.framesToHMSF(startOffset),
stopTrack, common.framesToHMSF(stopOffset)), stopTrack, common.framesToHMSF(stopOffset)),
self.path]) self.path])
logger.debug('Running %s' % (" ".join(argv), )) logger.debug('Running %s' % (" ".join(argv), ))
try: try:
self._popen = asyncsub.Popen(argv, self._popen = asyncsub.Popen(argv,
bufsize=bufsize, bufsize=bufsize,
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stdin=subprocess.PIPE,
stderr=subprocess.PIPE, close_fds=True) stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
close_fds=True)
except OSError, e: except OSError, e:
import errno import errno
if e.errno == errno.ENOENT: if e.errno == errno.ENOENT:
@@ -366,7 +367,7 @@ class ReadTrackTask(task.Task):
if size != expected: if size != expected:
# FIXME: handle errors better # FIXME: handle errors better
logger.warning('file size %d did not match expected size %d', logger.warning('file size %d did not match expected size %d',
size, expected) size, expected)
if (size - expected) % common.BYTES_PER_FRAME == 0: if (size - expected) % common.BYTES_PER_FRAME == 0:
logger.warning('%d frames difference' % ( logger.warning('%d frames difference' % (
(size - expected) / common.BYTES_PER_FRAME)) (size - expected) / common.BYTES_PER_FRAME))
@@ -374,8 +375,10 @@ class ReadTrackTask(task.Task):
logger.warning('non-integral amount of frames difference') logger.warning('non-integral amount of frames difference')
self.setAndRaiseException(FileSizeError(self.path, self.setAndRaiseException(FileSizeError(self.path,
"File size %d did not match expected size %d" % ( "File size %d did not "
size, expected))) "match expected "
"size %d" % (
size, expected)))
if not self.exception and self._popen.returncode != 0: if not self.exception and self._popen.returncode != 0:
if self._errors: if self._errors:
@@ -461,10 +464,11 @@ class ReadVerifyTrackTask(task.MultiSeparateTask):
self.tasks = [] self.tasks = []
self.tasks.append( self.tasks.append(
ReadTrackTask(tmppath, table, start, stop, overread, ReadTrackTask(tmppath, table, start, stop, overread,
offset=offset, device=device, what=what)) offset=offset, device=device, what=what))
self.tasks.append(checksum.CRC32Task(tmppath)) self.tasks.append(checksum.CRC32Task(tmppath))
t = ReadTrackTask(tmppath, table, start, stop, overread, 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(t)
self.tasks.append(checksum.CRC32Task(tmppath)) self.tasks.append(checksum.CRC32Task(tmppath))
@@ -503,7 +507,7 @@ class ReadVerifyTrackTask(task.MultiSeparateTask):
try: try:
if not self.exception: if not self.exception:
self.quality = max(self.tasks[0].quality, self.quality = max(self.tasks[0].quality,
self.tasks[2].quality) self.tasks[2].quality)
self.peak = self.tasks[6].peak self.peak = self.tasks[6].peak
logger.debug('peak: %r', self.peak) logger.debug('peak: %r', self.peak)
self.testspeed = self.tasks[0].speed self.testspeed = self.tasks[0].speed
@@ -535,9 +539,8 @@ class ReadVerifyTrackTask(task.MultiSeparateTask):
logger.debug('Moving to final path %r', self.path) logger.debug('Moving to final path %r', self.path)
os.rename(self._tmppath, self.path) os.rename(self._tmppath, self.path)
except Exception, e: except Exception, e:
logger.debug('Exception while moving to final path %r: ' logger.debug('Exception while moving to final '
'%r', 'path %r: %r', self.path, str(e))
self.path, str(e))
self.exception = e self.exception = e
else: else:
os.unlink(self._tmppath) os.unlink(self._tmppath)
@@ -548,15 +551,16 @@ class ReadVerifyTrackTask(task.MultiSeparateTask):
task.MultiSeparateTask.stop(self) task.MultiSeparateTask.stop(self)
_VERSION_RE = re.compile( _VERSION_RE = re.compile(
"^cdparanoia (?P<version>.+) release (?P<release>.+) \(.*\)") "^cdparanoia (?P<version>.+) release (?P<release>.+) \(.*\)")
def getCdParanoiaVersion(): def getCdParanoiaVersion():
getter = common.VersionGetter('cdparanoia', getter = common.VersionGetter('cdparanoia',
["cdparanoia", "-V"], ["cdparanoia", "-V"],
_VERSION_RE, _VERSION_RE,
"%(version)s %(release)s") "%(version)s %(release)s")
return getter.get() return getter.get()

View File

@@ -11,6 +11,7 @@ logger = logging.getLogger(__name__)
CDRDAO = 'cdrdao' CDRDAO = 'cdrdao'
def read_toc(device, fast_toc=False): def read_toc(device, fast_toc=False):
""" """
Return cdrdao-generated table of contents for 'device'. Return cdrdao-generated table of contents for 'device'.
@@ -26,7 +27,7 @@ def read_toc(device, fast_toc=False):
os.unlink(tocfile) os.unlink(tocfile)
cmd = [CDRDAO, 'read-toc'] + (['--fast-toc'] if fast_toc else []) + [ 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 # PIPE is the closest to >/dev/null we can get
logger.debug("executing %r", cmd) logger.debug("executing %r", cmd)
p = Popen(cmd, stdout=PIPE, stderr=PIPE) p = Popen(cmd, stdout=PIPE, stderr=PIPE)
@@ -45,6 +46,7 @@ def read_toc(device, fast_toc=False):
os.unlink(tocfile) os.unlink(tocfile)
return toc return toc
def version(): def version():
""" """
Return cdrdao version as a string. Return cdrdao version as a string.
@@ -56,25 +58,28 @@ def version():
"return code is " + str(cdrdao.returncode)) "return code is " + str(cdrdao.returncode))
return None return None
m = re.compile(r'^Cdrdao version (?P<version>.*) - \(C\)').search( m = re.compile(r'^Cdrdao version (?P<version>.*) - \(C\)').search(
err.decode('utf-8')) err.decode('utf-8'))
if not m: if not m:
logger.warning("cdrdao version detection failed: " logger.warning("cdrdao version detection failed: "
"could not find version") "could not find version")
return None return None
return m.group('version') return m.group('version')
def ReadTOCTask(device): def ReadTOCTask(device):
""" """
stopgap morituri-insanity compatibility layer stopgap morituri-insanity compatibility layer
""" """
return read_toc(device, fast_toc=True) return read_toc(device, fast_toc=True)
def ReadTableTask(device): def ReadTableTask(device):
""" """
stopgap morituri-insanity compatibility layer stopgap morituri-insanity compatibility layer
""" """
return read_toc(device) return read_toc(device)
def getCDRDAOVersion(): def getCDRDAOVersion():
""" """
stopgap morituri-insanity compatibility layer stopgap morituri-insanity compatibility layer

View File

@@ -3,6 +3,7 @@ from subprocess import check_call, CalledProcessError
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def encode(infile, outfile): def encode(infile, outfile):
""" """
Encodes infile to outfile, with flac. Encodes infile to outfile, with flac.

View File

@@ -6,6 +6,7 @@ logger = logging.getLogger(__name__)
SOX = 'sox' SOX = 'sox'
def peak_level(track_path): def peak_level(track_path):
""" """
Accepts a path to a sox-decodable audio file. Accepts a path to a sox-decodable audio file.

View File

@@ -8,6 +8,7 @@ logger = logging.getLogger(__name__)
SOXI = 'soxi' SOXI = 'soxi'
class AudioLengthTask(ctask.PopenTask): class AudioLengthTask(ctask.PopenTask):
""" """
I calculate the length of a track in audio samples. I calculate the length of a track in audio samples.
@@ -41,7 +42,7 @@ class AudioLengthTask(ctask.PopenTask):
self._error.append(bytes) self._error.append(bytes)
def failed(self): def failed(self):
self.setException(Exception("soxi failed: %s"%"".join(self._error))) self.setException(Exception("soxi failed: %s" % "".join(self._error)))
def done(self): def done(self):
if self._error: if self._error:

View File

@@ -131,7 +131,7 @@ class WhipperLogger(result.Logger):
accurateTracks = nonHTOA - self._accuratelyRipped accurateTracks = nonHTOA - self._accuratelyRipped
lines.append("%s Some tracks could not be verified as " lines.append("%s Some tracks could not be verified as "
"accurate (%d/%d got no match)" % ( "accurate (%d/%d got no match)" % (
arHeading, accurateTracks, nonHTOA)) arHeading, accurateTracks, nonHTOA))
else: else:
lines.append("%s All tracks accurately ripped" % arHeading) lines.append("%s All tracks accurately ripped" % arHeading)

View File

@@ -54,7 +54,7 @@ class TrackResult:
""" """
number = None number = None
filename = None filename = None
pregap = 0 # in frames pregap = 0 # in frames
pre_emphasis = None pre_emphasis = None
peak = 0.0 peak = 0.0
@@ -65,7 +65,7 @@ class TrackResult:
copyduration = 0.0 copyduration = 0.0
testcrc = None testcrc = None
copycrc = None copycrc = None
accurip = False # whether it's in the database accurip = False # whether it's in the database
ARCRC = None ARCRC = None
ARDBCRC = None ARDBCRC = None
ARDBConfidence = None ARDBConfidence = None

View File

@@ -53,12 +53,12 @@ class TestCase(unittest.TestCase):
return inst return inst
except exception, e: except exception, e:
raise Exception('%s raised instead of %s:\n %s' % 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: else:
raise Exception('%s not raised (%r returned)' % raise Exception('%s not raised (%r returned)' %
(exception.__name__, result) (exception.__name__, result)
) )
assertRaises = failUnlessRaises assertRaises = failUnlessRaises
@@ -67,8 +67,8 @@ class TestCase(unittest.TestCase):
Read a .cue file, and replace the version comment with the current Read a .cue file, and replace the version comment with the current
version so we can use it in comparisons. version so we can use it in comparisons.
""" """
ret = open(os.path.join(os.path.dirname(__file__), name)).read( cuefile = os.path.join(os.path.dirname(__file__), name)
).decode('utf-8') ret = open(cuefile).read().decode('utf-8')
ret = re.sub( ret = re.sub(
'REM COMMENT "whipper.*', 'REM COMMENT "whipper.*',
'REM COMMENT "whipper %s"' % (whipper.__version__), 'REM COMMENT "whipper %s"' % (whipper.__version__),
@@ -76,6 +76,7 @@ class TestCase(unittest.TestCase):
return ret return ret
class UnicodeTestMixin: class UnicodeTestMixin:
# A helper mixin to skip tests if we're not in a UTF-8 locale # A helper mixin to skip tests if we're not in a UTF-8 locale

View File

@@ -12,13 +12,12 @@ class AccurateRipResponseTestCase(tcommon.TestCase):
def testResponse(self): def testResponse(self):
path = os.path.join(os.path.dirname(__file__), 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() data = open(path, "rb").read()
responses = accurip.getAccurateRipResponses(data) responses = accurip.getAccurateRipResponses(data)
self.assertEquals(len(responses), 3) self.assertEquals(len(responses), 3)
response = responses[0] response = responses[0]
self.assertEquals(response.trackCount, 11) self.assertEquals(response.trackCount, 11)

View File

@@ -13,12 +13,12 @@ class ShrinkTestCase(tcommon.TestCase):
def testSufjan(self): def testSufjan(self):
path = (u'whipper/Sufjan Stevens - Illinois/02. Sufjan Stevens - ' path = (u'whipper/Sufjan Stevens - Illinois/02. Sufjan Stevens - '
'The Black Hawk War, or, How to Demolish an Entire ' 'The Black Hawk War, or, How to Demolish an Entire '
'Civilization and Still Feel Good About Yourself in the ' 'Civilization and Still Feel Good About Yourself in the '
'Morning, or, We Apologize for the Inconvenience but ' 'Morning, or, We Apologize for the Inconvenience but '
'You\'re Going to Have to Leave Now, or, "I Have Fought ' 'You\'re Going to Have to Leave Now, or, "I Have Fought '
'the Big Knives and Will Continue to Fight Them Until They ' 'the Big Knives and Will Continue to Fight Them Until They '
'Are Off Our Lands!".flac') 'Are Off Our Lands!".flac')
shorter = common.shrinkPath(path) shorter = common.shrinkPath(path)
self.failUnless(os.path.splitext(path)[0].startswith( self.failUnless(os.path.splitext(path)[0].startswith(
@@ -46,7 +46,7 @@ class GetRelativePathTestCase(tcommon.TestCase):
track = './' + directory + '/01. Placebo - Taste in Men.flac' track = './' + directory + '/01. Placebo - Taste in Men.flac'
self.assertEquals(common.getRelativePath(track, cue), self.assertEquals(common.getRelativePath(track, cue),
'01. Placebo - Taste in Men.flac') '01. Placebo - Taste in Men.flac')
class GetRealPathTestCase(tcommon.TestCase): class GetRealPathTestCase(tcommon.TestCase):
@@ -56,12 +56,12 @@ class GetRealPathTestCase(tcommon.TestCase):
refPath = os.path.join(os.path.dirname(path), 'fake.cue') refPath = os.path.join(os.path.dirname(path), 'fake.cue')
self.assertEquals(common.getRealPath(refPath, path), self.assertEquals(common.getRealPath(refPath, path),
path) path)
# same path, but with wav extension, will point to flac file # same path, but with wav extension, will point to flac file
wavPath = path[:-4] + 'wav' wavPath = path[:-4] + 'wav'
self.assertEquals(common.getRealPath(refPath, wavPath), self.assertEquals(common.getRealPath(refPath, wavPath),
path) path)
os.close(fd) os.close(fd)
os.unlink(path) os.unlink(path)

View File

@@ -20,24 +20,24 @@ class ConfigTestCase(tcommon.TestCase):
os.unlink(self._path) os.unlink(self._path)
def testAddReadOffset(self): def testAddReadOffset(self):
self.assertRaises(KeyError, self.assertRaises(KeyError, self._config.getReadOffset,
self._config.getReadOffset, 'PLEXTOR ', 'DVDR PX-L890SA', '1.05') 'PLEXTOR ', 'DVDR PX-L890SA', '1.05')
self._config.setReadOffset('PLEXTOR ', 'DVDR PX-L890SA', '1.05', 6) self._config.setReadOffset('PLEXTOR ', 'DVDR PX-L890SA', '1.05', 6)
# getting it from memory should work # getting it from memory should work
offset = self._config.getReadOffset('PLEXTOR ', 'DVDR PX-L890SA', offset = self._config.getReadOffset(
'1.05') 'PLEXTOR ', 'DVDR PX-L890SA', '1.05')
self.assertEquals(offset, 6) self.assertEquals(offset, 6)
# and so should getting it after reading it again # and so should getting it after reading it again
self._config.open() self._config.open()
offset = self._config.getReadOffset('PLEXTOR ', 'DVDR PX-L890SA', offset = self._config.getReadOffset(
'1.05') 'PLEXTOR ', 'DVDR PX-L890SA', '1.05')
self.assertEquals(offset, 6) self.assertEquals(offset, 6)
def testAddReadOffsetSpaced(self): def testAddReadOffsetSpaced(self):
self.assertRaises(KeyError, self.assertRaises(KeyError, self._config.getReadOffset,
self._config.getReadOffset, 'Slimtype', 'eSAU208 2 ', 'ML03') 'Slimtype', 'eSAU208 2 ', 'ML03')
self._config.setReadOffset('Slimtype', 'eSAU208 2 ', 'ML03', 6) self._config.setReadOffset('Slimtype', 'eSAU208 2 ', 'ML03', 6)
# getting it from memory should work # getting it from memory should work
@@ -53,7 +53,7 @@ class ConfigTestCase(tcommon.TestCase):
def testDefeatsCache(self): def testDefeatsCache(self):
self.assertRaises(KeyError, self._config.getDefeatsCache, self.assertRaises(KeyError, self._config.getDefeatsCache,
'PLEXTOR ', 'DVDR PX-L890SA', '1.05') 'PLEXTOR ', 'DVDR PX-L890SA', '1.05')
self._config.setDefeatsCache( self._config.setDefeatsCache(
'PLEXTOR ', 'DVDR PX-L890SA', '1.05', False) 'PLEXTOR ', 'DVDR PX-L890SA', '1.05', False)

View File

@@ -13,8 +13,8 @@ class MetadataTestCase(unittest.TestCase):
# Generated with rip -R cd info # Generated with rip -R cd info
def testJeffEverybodySingle(self): def testJeffEverybodySingle(self):
path = os.path.join(os.path.dirname(__file__), filename = 'whipper.release.3451f29c-9bb8-4cc5-bfcc-bd50104b94f8.json'
'whipper.release.3451f29c-9bb8-4cc5-bfcc-bd50104b94f8.json') path = os.path.join(os.path.dirname(__file__), filename)
handle = open(path, "rb") handle = open(path, "rb")
response = json.loads(handle.read()) response = json.loads(handle.read())
handle.close() handle.close()
@@ -26,8 +26,8 @@ class MetadataTestCase(unittest.TestCase):
def test2MeterSessies10(self): def test2MeterSessies10(self):
# various artists, multiple artists per track # various artists, multiple artists per track
path = os.path.join(os.path.dirname(__file__), filename = 'whipper.release.a76714e0-32b1-4ed4-b28e-f86d99642193.json'
'whipper.release.a76714e0-32b1-4ed4-b28e-f86d99642193.json') path = os.path.join(os.path.dirname(__file__), filename)
handle = open(path, "rb") handle = open(path, "rb")
response = json.loads(handle.read()) response = json.loads(handle.read())
handle.close() handle.close()
@@ -38,7 +38,7 @@ class MetadataTestCase(unittest.TestCase):
self.assertEquals(metadata.artist, u'Various Artists') self.assertEquals(metadata.artist, u'Various Artists')
self.assertEquals(metadata.release, u'2001-10-15') self.assertEquals(metadata.release, u'2001-10-15')
self.assertEquals(metadata.mbidArtist, self.assertEquals(metadata.mbidArtist,
u'89ad4ac3-39f7-470e-963a-56509c546377') u'89ad4ac3-39f7-470e-963a-56509c546377')
self.assertEquals(len(metadata.tracks), 18) self.assertEquals(len(metadata.tracks), 18)
@@ -46,16 +46,16 @@ class MetadataTestCase(unittest.TestCase):
self.assertEquals(track16.artist, 'Tom Jones & Stereophonics') self.assertEquals(track16.artist, 'Tom Jones & Stereophonics')
self.assertEquals(track16.mbidArtist, self.assertEquals(track16.mbidArtist,
u'57c6f649-6cde-48a7-8114-2a200247601a' u'57c6f649-6cde-48a7-8114-2a200247601a'
';0bfba3d3-6a04-4779-bb0a-df07df5b0558' ';0bfba3d3-6a04-4779-bb0a-df07df5b0558'
) )
self.assertEquals(track16.sortName, self.assertEquals(track16.sortName,
u'Jones, Tom & Stereophonics') u'Jones, Tom & Stereophonics')
def testBalladOfTheBrokenSeas(self): def testBalladOfTheBrokenSeas(self):
# various artists disc # various artists disc
path = os.path.join(os.path.dirname(__file__), filename = 'whipper.release.e32ae79a-336e-4d33-945c-8c5e8206dbd3.json'
'whipper.release.e32ae79a-336e-4d33-945c-8c5e8206dbd3.json') path = os.path.join(os.path.dirname(__file__), filename)
handle = open(path, "rb") handle = open(path, "rb")
response = json.loads(handle.read()) response = json.loads(handle.read())
handle.close() handle.close()
@@ -65,11 +65,11 @@ class MetadataTestCase(unittest.TestCase):
self.assertEquals(metadata.artist, u'Isobel Campbell & Mark Lanegan') self.assertEquals(metadata.artist, u'Isobel Campbell & Mark Lanegan')
self.assertEquals(metadata.sortName, 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.release, u'2006-01-30')
self.assertEquals(metadata.mbidArtist, self.assertEquals(metadata.mbidArtist,
u'd51f3a15-12a2-41a0-acfa-33b5eae71164;' u'd51f3a15-12a2-41a0-acfa-33b5eae71164;'
'a9126556-f555-4920-9617-6e013f8228a7') 'a9126556-f555-4920-9617-6e013f8228a7')
self.assertEquals(len(metadata.tracks), 12) 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.artist, u'Isobel Campbell & Mark Lanegan')
self.assertEquals(track12.sortName, self.assertEquals(track12.sortName,
u'Campbell, Isobel' u'Campbell, Isobel'
' & Lanegan, Mark' ' & Lanegan, Mark'
) )
self.assertEquals(track12.mbidArtist, self.assertEquals(track12.mbidArtist,
u'd51f3a15-12a2-41a0-acfa-33b5eae71164;' u'd51f3a15-12a2-41a0-acfa-33b5eae71164;'
'a9126556-f555-4920-9617-6e013f8228a7') 'a9126556-f555-4920-9617-6e013f8228a7')
def testMalaInCuba(self): def testMalaInCuba(self):
# single artist disc, but with multiple artists tracks # single artist disc, but with multiple artists tracks
# see https://github.com/thomasvs/morituri/issues/19 # see https://github.com/thomasvs/morituri/issues/19
path = os.path.join(os.path.dirname(__file__), filename = 'whipper.release.61c6fd9b-18f8-4a45-963a-ba3c5d990cae.json'
'whipper.release.61c6fd9b-18f8-4a45-963a-ba3c5d990cae.json') path = os.path.join(os.path.dirname(__file__), filename)
handle = open(path, "rb") handle = open(path, "rb")
response = json.loads(handle.read()) response = json.loads(handle.read())
handle.close() handle.close()
@@ -100,7 +100,7 @@ class MetadataTestCase(unittest.TestCase):
self.assertEquals(metadata.sortName, u'Mala') self.assertEquals(metadata.sortName, u'Mala')
self.assertEquals(metadata.release, u'2012-09-17') self.assertEquals(metadata.release, u'2012-09-17')
self.assertEquals(metadata.mbidArtist, self.assertEquals(metadata.mbidArtist,
u'09f221eb-c97e-4da5-ac22-d7ab7c555bbb') u'09f221eb-c97e-4da5-ac22-d7ab7c555bbb')
self.assertEquals(len(metadata.tracks), 14) 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.artist, u'Mala feat. Dreiser & Sexto Sentido')
self.assertEquals(track6.sortName, self.assertEquals(track6.sortName,
u'Mala feat. Dreiser & Sexto Sentido') u'Mala feat. Dreiser & Sexto Sentido')
self.assertEquals(track6.mbidArtist, self.assertEquals(track6.mbidArtist,
u'09f221eb-c97e-4da5-ac22-d7ab7c555bbb' u'09f221eb-c97e-4da5-ac22-d7ab7c555bbb'
';ec07a209-55ff-4084-bc41-9d4d1764e075' ';ec07a209-55ff-4084-bc41-9d4d1764e075'
';f626b92e-07b1-4a19-ad13-c09d690db66c' ';f626b92e-07b1-4a19-ad13-c09d690db66c'
) )

View File

@@ -22,9 +22,9 @@ class FilterTestCase(common.TestCase):
def testSpecial(self): def testSpecial(self):
part = u'<<< $&*!\' "()`{}[]spaceship>>>' part = u'<<< $&*!\' "()`{}[]spaceship>>>'
self.assertEquals(self._filter.filter(part), self.assertEquals(self._filter.filter(part),
u'___ _____ ________spaceship___') u'___ _____ ________spaceship___')
def testGreatest(self): def testGreatest(self):
part = u'Greatest Ever! Soul: The Definitive Collection' part = u'Greatest Ever! Soul: The Definitive Collection'
self.assertEquals(self._filter.filter(part), self.assertEquals(self._filter.filter(part),
u'Greatest Ever_ Soul - The Definitive Collection') u'Greatest Ever_ Soul - The Definitive Collection')

View File

@@ -19,15 +19,16 @@ class TrackImageVerifyTestCase(unittest.TestCase):
def testVerify(self): def testVerify(self):
path = os.path.join(os.path.dirname(__file__), 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() data = open(path, "rb").read()
responses = accurip.getAccurateRipResponses(data) responses = accurip.getAccurateRipResponses(data)
# these crc's were calculated from an actual rip # these crc's were calculated from an actual rip
checksums = [1644890007, 2945205445, 3983436658, 1528082495, checksums = [1644890007, 2945205445, 3983436658, 1528082495,
1203704270, 1163423644, 3649097244, 100524219, 1583356174, 373652058, 1203704270, 1163423644, 3649097244, 100524219,
1842579359, 2850056507, 1329730252, 2526965856, 2525886806, 209743350, 1583356174, 373652058, 1842579359, 2850056507,
3184062337, 2099956663, 2943874164, 2321637196] 1329730252, 2526965856, 2525886806, 209743350,
3184062337, 2099956663, 2943874164, 2321637196]
prog = program.Program(config.Config()) prog = program.Program(config.Config())
prog.result = result.RipResult() prog.result = result.RipResult()
@@ -59,21 +60,21 @@ class TrackImageVerifyTestCase(unittest.TestCase):
res = prog.getAccurateRipResults() res = prog.getAccurateRipResults()
self.assertEquals(res[1 - 1], self.assertEquals(res[1 - 1],
"Track 1: rip NOT accurate (not found) " "Track 1: rip NOT accurate (not found) "
"[620b0797], DB [notfound]") "[620b0797], DB [notfound]")
self.assertEquals(res[2 - 1], self.assertEquals(res[2 - 1],
"Track 2: rip accurate (max confidence 2) " "Track 2: rip accurate (max confidence 2) "
"[af8c44c5], DB [af8c44c5]") "[af8c44c5], DB [af8c44c5]")
self.assertEquals(res[10 - 1], self.assertEquals(res[10 - 1],
"Track 10: rip NOT accurate (max confidence 2) " "Track 10: rip NOT accurate (max confidence 2) "
"[16457a5a], DB [eb6e55b4]") "[16457a5a], DB [eb6e55b4]")
class HTOATestCase(unittest.TestCase): class HTOATestCase(unittest.TestCase):
def setUp(self): def setUp(self):
path = os.path.join(os.path.dirname(__file__), path = os.path.join(os.path.dirname(__file__),
'silentalarm.result.pickle') 'silentalarm.result.pickle')
self._tracks = pickle.load(open(path, 'rb')) self._tracks = pickle.load(open(path, 'rb'))
def testGetAccurateRipResults(self): def testGetAccurateRipResults(self):
@@ -90,9 +91,10 @@ class PathTestCase(unittest.TestCase):
prog = program.Program(config.Config()) prog = program.Program(config.Config())
path = prog.getPath(u'/tmp', DEFAULT_DISC_TEMPLATE, path = prog.getPath(u'/tmp', DEFAULT_DISC_TEMPLATE,
'mbdiscid', 0) 'mbdiscid', 0)
self.assertEquals(path, self.assertEquals(path,
u'/tmp/unknown/Unknown Artist - mbdiscid/Unknown Artist - mbdiscid') unicode('/tmp/unknown/Unknown Artist - mbdiscid/'
'Unknown Artist - mbdiscid'))
def testStandardTemplateFilled(self): def testStandardTemplateFilled(self):
prog = program.Program(config.Config()) prog = program.Program(config.Config())
@@ -102,9 +104,10 @@ class PathTestCase(unittest.TestCase):
prog.metadata = md prog.metadata = md
path = prog.getPath(u'/tmp', DEFAULT_DISC_TEMPLATE, path = prog.getPath(u'/tmp', DEFAULT_DISC_TEMPLATE,
'mbdiscid', 0) 'mbdiscid', 0)
self.assertEquals(path, self.assertEquals(path,
u'/tmp/unknown/Jeff Buckley - Grace/Jeff Buckley - Grace') unicode('/tmp/unknown/Jeff Buckley - Grace/'
'Jeff Buckley - Grace'))
def testIssue66TemplateFilled(self): def testIssue66TemplateFilled(self):
prog = program.Program(config.Config()) prog = program.Program(config.Config())
@@ -115,4 +118,4 @@ class PathTestCase(unittest.TestCase):
path = prog.getPath(u'/tmp', u'%A/%d', 'mbdiscid', 0) path = prog.getPath(u'/tmp', u'%A/%d', 'mbdiscid', 0)
self.assertEquals(path, self.assertEquals(path,
u'/tmp/Jeff Buckley/Grace') u'/tmp/Jeff Buckley/Grace')

View File

@@ -11,11 +11,12 @@ from whipper.image import table, cue
from whipper.test import common from whipper.test import common
class KingsSingleTestCase(unittest.TestCase): class KingsSingleTestCase(unittest.TestCase):
def setUp(self): def setUp(self):
self.cue = cue.CueFile(os.path.join(os.path.dirname(__file__), self.cue = cue.CueFile(os.path.join(os.path.dirname(__file__),
u'kings-single.cue')) u'kings-single.cue'))
self.cue.parse() self.cue.parse()
self.assertEquals(len(self.cue.table.tracks), 11) self.assertEquals(len(self.cue.table.tracks), 11)
@@ -31,7 +32,7 @@ class KingsSeparateTestCase(unittest.TestCase):
def setUp(self): def setUp(self):
self.cue = cue.CueFile(os.path.join(os.path.dirname(__file__), self.cue = cue.CueFile(os.path.join(os.path.dirname(__file__),
u'kings-separate.cue')) u'kings-separate.cue'))
self.cue.parse() self.cue.parse()
self.assertEquals(len(self.cue.table.tracks), 11) self.assertEquals(len(self.cue.table.tracks), 11)
@@ -47,7 +48,7 @@ class KanyeMixedTestCase(unittest.TestCase):
def setUp(self): def setUp(self):
self.cue = cue.CueFile(os.path.join(os.path.dirname(__file__), self.cue = cue.CueFile(os.path.join(os.path.dirname(__file__),
u'kanye.cue')) u'kanye.cue'))
self.cue.parse() self.cue.parse()
self.assertEquals(len(self.cue.table.tracks), 13) self.assertEquals(len(self.cue.table.tracks), 13)
@@ -70,7 +71,7 @@ class WriteCueFileTestCase(unittest.TestCase):
t = table.Track(2) t = table.Track(2)
t.index(0, absolute=1000, path=u'track01.wav', 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) t.index(1, absolute=2000, path=u'track02.wav', relative=0, counter=2)
it.tracks.append(t) it.tracks.append(t)
it.absolutize() it.absolutize()

View File

@@ -36,7 +36,7 @@ class LadyhawkeTestCase(tcommon.TestCase):
self.table.tracks.append(table.Track(13, audio=False)) self.table.tracks.append(table.Track(13, audio=False))
offsets = [0, 15537, 31691, 50866, 66466, 81202, 99409, offsets = [0, 15537, 31691, 50866, 66466, 81202, 99409,
115920, 133093, 149847, 161560, 177682, 207106] 115920, 133093, 149847, 161560, 177682, 207106]
t = self.table.tracks t = self.table.tracks
for i, offset in enumerate(offsets): for i, offset in enumerate(offsets):
t[i].index(1, absolute=offset) t[i].index(1, absolute=offset)
@@ -59,14 +59,14 @@ class LadyhawkeTestCase(tcommon.TestCase):
# however, not (yet) in MusicBrainz database # however, not (yet) in MusicBrainz database
self.assertEquals(self.table.getMusicBrainzDiscId(), self.assertEquals(self.table.getMusicBrainzDiscId(),
"KnpGsLhvH.lPrNc1PBL21lb9Bg4-") "KnpGsLhvH.lPrNc1PBL21lb9Bg4-")
def testAccurateRip(self): def testAccurateRip(self):
self.assertEquals(self.table.getAccurateRipIds(), ( self.assertEquals(self.table.getAccurateRipIds(), (
"0013bd5a", "00b8d489")) "0013bd5a", "00b8d489"))
self.assertEquals(self.table.getAccurateRipURL(), self.assertEquals(self.table.getAccurateRipURL(),
"http://www.accuraterip.com/accuraterip/a/5/d/" "http://www.accuraterip.com/accuraterip/a/5/d/"
"dBAR-012-0013bd5a-00b8d489-c60af50d.bin") "dBAR-012-0013bd5a-00b8d489-c60af50d.bin")
def testDuration(self): def testDuration(self):
self.assertEquals(self.table.duration(), 2761413) self.assertEquals(self.table.duration(), 2761413)
@@ -95,7 +95,7 @@ class MusicBrainzTestCase(tcommon.TestCase):
def testMusicBrainz(self): def testMusicBrainz(self):
self.assertEquals(self.table.getMusicBrainzDiscId(), self.assertEquals(self.table.getMusicBrainzDiscId(),
'49HHV7Eb8UKF3aQiNmu1GR8vKTY-') '49HHV7Eb8UKF3aQiNmu1GR8vKTY-')
class PregapTestCase(tcommon.TestCase): class PregapTestCase(tcommon.TestCase):

View File

@@ -14,8 +14,7 @@ from whipper.test import common
class CureTestCase(common.TestCase): class CureTestCase(common.TestCase):
def setUp(self): def setUp(self):
self.path = os.path.join(os.path.dirname(__file__), self.path = os.path.join(os.path.dirname(__file__), u'cure.toc')
u'cure.toc')
self.toc = toc.TocFile(self.path) self.toc = toc.TocFile(self.path)
self.toc.parse() self.toc.parse()
self.assertEquals(len(self.toc.table.tracks), 13) 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 # its length is all of track 1 from .toc, plus the INDEX 00 length
# of track 2 # of track 2
self.assertEquals(self.toc.getTrackLength(t), 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 # last track has unknown length
t = self.toc.table.tracks[-1] t = self.toc.table.tracks[-1]
self.assertEquals(self.toc.getTrackLength(t), -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 # we verify it because it has failed in readdisc in the past
self.assertEquals(self.toc.table.getAccurateRipURL(), self.assertEquals(self.toc.table.getAccurateRipURL(),
'http://www.accuraterip.com/accuraterip/' 'http://www.accuraterip.com/accuraterip/3/c/4/dBAR-013-0019d4c3-00fe8924-b90c650d.bin') # noqa: E501
'3/c/4/dBAR-013-0019d4c3-00fe8924-b90c650d.bin')
def testGetRealPath(self): def testGetRealPath(self):
self.assertRaises(KeyError, self.toc.getRealPath, u'track01.wav') self.assertRaises(KeyError, self.toc.getRealPath, u'track01.wav')
@@ -110,8 +108,7 @@ class CureTestCase(common.TestCase):
class BlocTestCase(common.TestCase): class BlocTestCase(common.TestCase):
def setUp(self): def setUp(self):
self.path = os.path.join(os.path.dirname(__file__), self.path = os.path.join(os.path.dirname(__file__), u'bloc.toc')
u'bloc.toc')
self.toc = toc.TocFile(self.path) self.toc = toc.TocFile(self.path)
self.toc.parse() self.toc.parse()
self.assertEquals(len(self.toc.table.tracks), 13) self.assertEquals(len(self.toc.table.tracks), 13)
@@ -168,8 +165,7 @@ class BlocTestCase(common.TestCase):
def testAccurateRip(self): def testAccurateRip(self):
# we verify it because it has failed in readdisc in the past # we verify it because it has failed in readdisc in the past
self.assertEquals(self.toc.table.getAccurateRipURL(), self.assertEquals(self.toc.table.getAccurateRipURL(),
'http://www.accuraterip.com/accuraterip/' 'http://www.accuraterip.com/accuraterip/e/d/2/dBAR-013-001af2de-0105994e-ad0be00d.bin') # noqa: E501
'e/d/2/dBAR-013-001af2de-0105994e-ad0be00d.bin')
# The Breeders - Mountain Battles has CDText # The Breeders - Mountain Battles has CDText
@@ -177,8 +173,7 @@ class BlocTestCase(common.TestCase):
class BreedersTestCase(common.TestCase): class BreedersTestCase(common.TestCase):
def setUp(self): def setUp(self):
self.path = os.path.join(os.path.dirname(__file__), self.path = os.path.join(os.path.dirname(__file__), u'breeders.toc')
u'breeders.toc')
self.toc = toc.TocFile(self.path) self.toc = toc.TocFile(self.path)
self.toc.parse() self.toc.parse()
self.assertEquals(len(self.toc.table.tracks), 13) self.assertEquals(len(self.toc.table.tracks), 13)
@@ -194,7 +189,6 @@ class BreedersTestCase(common.TestCase):
self.assertEquals(cdt['TITLE'], 'OVERGLAZED') self.assertEquals(cdt['TITLE'], 'OVERGLAZED')
def testConvertCue(self): def testConvertCue(self):
# self.toc.table.absolutize()
self.failUnless(self.toc.table.hasTOC()) self.failUnless(self.toc.table.hasTOC())
cue = self.toc.table.cue() cue = self.toc.table.cue()
ref = self.readCue('breeders.cue') ref = self.readCue('breeders.cue')
@@ -206,8 +200,7 @@ class BreedersTestCase(common.TestCase):
class LadyhawkeTestCase(common.TestCase): class LadyhawkeTestCase(common.TestCase):
def setUp(self): def setUp(self):
self.path = os.path.join(os.path.dirname(__file__), self.path = os.path.join(os.path.dirname(__file__), u'ladyhawke.toc')
u'ladyhawke.toc')
self.toc = toc.TocFile(self.path) self.toc = toc.TocFile(self.path)
self.toc.parse() self.toc.parse()
self.assertEquals(len(self.toc.table.tracks), 13) self.assertEquals(len(self.toc.table.tracks), 13)
@@ -221,12 +214,9 @@ class LadyhawkeTestCase(common.TestCase):
def testMusicBrainz(self): def testMusicBrainz(self):
self.assertEquals(self.toc.table.getMusicBrainzDiscId(), self.assertEquals(self.toc.table.getMusicBrainzDiscId(),
"KnpGsLhvH.lPrNc1PBL21lb9Bg4-") "KnpGsLhvH.lPrNc1PBL21lb9Bg4-")
self.assertEquals(self.toc.table.getMusicBrainzSubmitURL(), self.assertEquals(self.toc.table.getMusicBrainzSubmitURL(),
"https://musicbrainz.org/cdtoc/attach?toc=" "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
"1+12+195856+150+15687+31841+51016+66616+81352+99559+"
"116070+133243+149997+161710+177832&"
"tracks=12&id=KnpGsLhvH.lPrNc1PBL21lb9Bg4-")
# FIXME: I don't trust this toc, but I can't find the CD anymore # 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): def setUp(self):
self.toc1 = toc.TocFile(os.path.join(os.path.dirname(__file__), self.toc1 = toc.TocFile(os.path.join(os.path.dirname(__file__),
u'capital.1.toc')) u'capital.1.toc'))
self.toc1.parse() self.toc1.parse()
self.assertEquals(len(self.toc1.table.tracks), 11) self.assertEquals(len(self.toc1.table.tracks), 11)
self.failUnless(self.toc1.table.tracks[-1].audio) self.failUnless(self.toc1.table.tracks[-1].audio)
self.toc2 = toc.TocFile(os.path.join(os.path.dirname(__file__), self.toc2 = toc.TocFile(os.path.join(os.path.dirname(__file__),
u'capital.2.toc')) u'capital.2.toc'))
self.toc2.parse() self.toc2.parse()
self.assertEquals(len(self.toc2.table.tracks), 1) self.assertEquals(len(self.toc2.table.tracks), 1)
self.failIf(self.toc2.table.tracks[-1].audio) 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+ # 197850+24320+44855+64090+77885+88095+104020+118245+129255+141765+
# 164487+181780&tracks=11&id=MAj3xXf6QMy7G.BIFOyHyq4MySE- # 164487+181780&tracks=11&id=MAj3xXf6QMy7G.BIFOyHyq4MySE-
self.assertEquals(self.table.getMusicBrainzDiscId(), self.assertEquals(self.table.getMusicBrainzDiscId(),
"MAj3xXf6QMy7G.BIFOyHyq4MySE-") "MAj3xXf6QMy7G.BIFOyHyq4MySE-")
def testDuration(self): def testDuration(self):
# this matches track 11 end sector - track 1 start sector on # 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): class TOTBLTestCase(common.TestCase):
def setUp(self): def setUp(self):
self.path = os.path.join(os.path.dirname(__file__), self.path = os.path.join(os.path.dirname(__file__), u'totbl.fast.toc')
u'totbl.fast.toc')
self.toc = toc.TocFile(self.path) self.toc = toc.TocFile(self.path)
self.toc.parse() self.toc.parse()
self.assertEquals(len(self.toc.table.tracks), 11) self.assertEquals(len(self.toc.table.tracks), 11)
@@ -338,7 +327,7 @@ class StrokesTestCase(common.TestCase):
def setUp(self): def setUp(self):
self.path = os.path.join(os.path.dirname(__file__), 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 = toc.TocFile(self.path)
self.toc.parse() self.toc.parse()
self.assertEquals(len(self.toc.table.tracks), 1) self.assertEquals(len(self.toc.table.tracks), 1)
@@ -358,14 +347,16 @@ class StrokesTestCase(common.TestCase):
self.assertEquals(i1.path, u'data.wav') self.assertEquals(i1.path, u'data.wav')
cue = self._filterCue(self.toc.table.cue()) cue = self._filterCue(self.toc.table.cue())
ref = self._filterCue(open(os.path.join(os.path.dirname(__file__), ref = self._filterCue(
'strokes-someday.eac.cue')).read()).decode('utf-8') open(os.path.join(
os.path.dirname(__file__),
'strokes-someday.eac.cue')).read()).decode('utf-8')
common.diffStrings(ref, cue) common.diffStrings(ref, cue)
def _filterCue(self, output): def _filterCue(self, output):
# helper to be able to compare our generated .cue with the # helper to be able to compare our generated .cue with the
# EAC-extracted one # EAC-extracted one
discard = [ 'TITLE', 'PERFORMER', 'FLAGS', 'REM' ] discard = ['TITLE', 'PERFORMER', 'FLAGS', 'REM']
lines = output.split('\n') lines = output.split('\n')
res = [] res = []
@@ -385,21 +376,16 @@ class StrokesTestCase(common.TestCase):
return '\n'.join(res) return '\n'.join(res)
# Surfer Rosa has # Surfer Rosa has
# track 00 consisting of 32 frames of SILENCE # track 00 consisting of 32 frames of SILENCE
# track 11 Vamos with an INDEX 02 # track 11 Vamos with an INDEX 02
# compared to an EAC single .cue file, all our offsets are 32 frames off # 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 # because the toc uses silence for track 01 index 00 while EAC puts it in
# Range.wav # Range.wav
class SurferRosaTestCase(common.TestCase): class SurferRosaTestCase(common.TestCase):
def setUp(self): def setUp(self):
self.path = os.path.join(os.path.dirname(__file__), self.path = os.path.join(os.path.dirname(__file__), u'surferrosa.toc')
u'surferrosa.toc')
self.toc = toc.TocFile(self.path) self.toc = toc.TocFile(self.path)
self.toc.parse() self.toc.parse()
self.assertEquals(len(self.toc.table.tracks), 21) self.assertEquals(len(self.toc.table.tracks), 21)

View File

@@ -15,7 +15,7 @@ class ParseTestCase(common.TestCase):
def setUp(self): def setUp(self):
# report from Afghan Whigs - Sweet Son Of A Bitch # report from Afghan Whigs - Sweet Son Of A Bitch
path = os.path.join(os.path.dirname(__file__), path = os.path.join(os.path.dirname(__file__),
'cdparanoia.progress') 'cdparanoia.progress')
self._parser = cdparanoia.ProgressParser(start=45990, stop=47719) self._parser = cdparanoia.ProgressParser(start=45990, stop=47719)
self._handle = open(path) self._handle = open(path)
@@ -27,11 +27,12 @@ class ParseTestCase(common.TestCase):
q = '%.01f %%' % (self._parser.getTrackQuality() * 100.0, ) q = '%.01f %%' % (self._parser.getTrackQuality() * 100.0, )
self.assertEquals(q, '99.6 %') self.assertEquals(q, '99.6 %')
class Parse1FrameTestCase(common.TestCase): class Parse1FrameTestCase(common.TestCase):
def setUp(self): def setUp(self):
path = os.path.join(os.path.dirname(__file__), path = os.path.join(os.path.dirname(__file__),
'cdparanoia.progress.strokes') 'cdparanoia.progress.strokes')
self._parser = cdparanoia.ProgressParser(start=0, stop=0) self._parser = cdparanoia.ProgressParser(start=0, stop=0)
self._handle = open(path) self._handle = open(path)
@@ -49,7 +50,7 @@ class ErrorTestCase(common.TestCase):
def setUp(self): def setUp(self):
# report from a rip with offset -1164 causing scsi errors # report from a rip with offset -1164 causing scsi errors
path = os.path.join(os.path.dirname(__file__), path = os.path.join(os.path.dirname(__file__),
'cdparanoia.progress.error') 'cdparanoia.progress.error')
self._parser = cdparanoia.ProgressParser(start=0, stop=10800) self._parser = cdparanoia.ProgressParser(start=0, stop=10800)
self._handle = open(path) self._handle = open(path)
@@ -87,7 +88,7 @@ class CacheTestCase(common.TestCase):
self.runner = task.SyncRunner(verbose=False) self.runner = task.SyncRunner(verbose=False)
path = os.path.join(os.path.dirname(__file__), path = os.path.join(os.path.dirname(__file__),
'cdparanoia', 'PX-L890SA.cdparanoia-A.stderr') 'cdparanoia', 'PX-L890SA.cdparanoia-A.stderr')
t = AnalyzeFileTask(path) t = AnalyzeFileTask(path)
self.runner.run(t) self.runner.run(t)
self.failUnless(t.defeatsCache) self.failUnless(t.defeatsCache)

View File

@@ -1,14 +1,12 @@
# -*- Mode: Python; test-case-name: whipper.test.test_program_cdparanoia -*- # -*- Mode: Python; test-case-name: whipper.test.test_program_cdparanoia -*-
# vi:si:et:sw=4:sts=4:ts=4 # vi:si:et:sw=4:sts=4:ts=4
import os
from whipper.program import cdrdao from whipper.program import cdrdao
from whipper.test import common from whipper.test import common
# TODO: Current test architecture makes testing cdrdao difficult. Revisit. # TODO: Current test architecture makes testing cdrdao difficult. Revisit.
class VersionTestCase(common.TestCase): class VersionTestCase(common.TestCase):
def testGetVersion(self): def testGetVersion(self):
v = cdrdao.getCDRDAOVersion() v = cdrdao.getCDRDAOVersion()

View File

@@ -5,6 +5,7 @@ import os
from whipper.program import sox from whipper.program import sox
from whipper.test import common from whipper.test import common
class PeakLevelTestCase(common.TestCase): class PeakLevelTestCase(common.TestCase):
def setUp(self): def setUp(self):
self.path = os.path.join(os.path.dirname(__file__), 'track.flac') self.path = os.path.join(os.path.dirname(__file__), 'track.flac')

View File

@@ -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_file = os.path.join(os.path.dirname(__file__), u'track.flac')
base_track_length = 10 * common.SAMPLES_PER_FRAME base_track_length = 10 * common.SAMPLES_PER_FRAME
class AudioLengthTestCase(tcommon.TestCase): class AudioLengthTestCase(tcommon.TestCase):
def testLength(self): def testLength(self):
@@ -34,6 +35,7 @@ class AudioLengthPathTestCase(tcommon.TestCase):
self.assertEquals(t.length, base_track_length) self.assertEquals(t.length, base_track_length)
os.unlink(path) os.unlink(path)
class NormalAudioLengthPathTestCase(AudioLengthPathTestCase): class NormalAudioLengthPathTestCase(AudioLengthPathTestCase):
def testSingleQuote(self): def testSingleQuote(self):
@@ -46,12 +48,13 @@ class NormalAudioLengthPathTestCase(AudioLengthPathTestCase):
class UnicodeAudioLengthPathTestCase(AudioLengthPathTestCase, class UnicodeAudioLengthPathTestCase(AudioLengthPathTestCase,
tcommon.UnicodeTestMixin): tcommon.UnicodeTestMixin):
def testUnicodePath(self): def testUnicodePath(self):
# this test makes sure we can checksum a unicode path # this test makes sure we can checksum a unicode path
self._testSuffix(u'whipper.test.B\xeate Noire.empty.flac') self._testSuffix(u'whipper.test.B\xeate Noire.empty.flac')
class AbsentFileAudioLengthPathTestCase(AudioLengthPathTestCase): class AbsentFileAudioLengthPathTestCase(AudioLengthPathTestCase):
def testAbsentFile(self): def testAbsentFile(self):
tempdir = tempfile.mkdtemp() tempdir = tempfile.mkdtemp()
@@ -60,6 +63,6 @@ class AbsentFileAudioLengthPathTestCase(AudioLengthPathTestCase):
t = AudioLengthTask(path) t = AudioLengthTask(path)
runner = task.SyncRunner() runner = task.SyncRunner()
self.assertRaises(task.TaskException, runner.run, self.assertRaises(task.TaskException, runner.run,
t, verbose=False) t, verbose=False)
os.rmdir(tempdir) os.rmdir(tempdir)