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)
offsets = {} # offset -> total count
offsets = {} # offset -> total count
rows = soup.findAll('tr')
for row in rows:

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

@@ -17,6 +17,7 @@ from whipper.program.utils import eject_device
import logging
logger = logging.getLogger(__name__)
def main():
# set user agent
musicbrainzngs.set_useragent("whipper", whipper.__version__,
@@ -47,7 +48,7 @@ def main():
if isinstance(e.exception, common.EmptyError):
logger.debug("EmptyError: %r", str(e.exception))
sys.stderr.write('whipper: error: Could not create encoded file.\n')
sys.stderr.write('whipper: error: Could not create encoded file.\n') # noqa: E501
return 255
# in python3 we can instead do `raise e.exception` as that would show
@@ -56,6 +57,7 @@ def main():
return 255
return ret if ret else 0
class Whipper(BaseCommand):
description = """whipper is a CD ripping utility focusing on accuracy over speed.
@@ -74,18 +76,20 @@ You can get help on subcommands by using the -h option to the subcommand.
def add_arguments(self):
self.parser.add_argument('-R', '--record',
action='store_true', dest='record',
help="record API requests for playback")
action='store_true', dest='record',
help="record API requests for playback")
self.parser.add_argument('-v', '--version',
action="store_true", dest="version",
help="show version information")
action="store_true", dest="version",
help="show version information")
self.parser.add_argument('-h', '--help',
action="store_true", dest="help",
help="show this help message and exit")
action="store_true", dest="help",
help="show this help message and exit")
self.parser.add_argument('-e', '--eject',
action="store", dest="eject", default="always",
choices=('never', 'failure', 'success', 'always'),
help="when to eject disc (default: always)")
action="store", dest="eject",
default="always",
choices=('never', 'failure',
'success', 'always'),
help="when to eject disc (default: always)")
def handle_arguments(self):
if self.options.help:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -29,6 +29,7 @@ from whipper.program import flac
import logging
logger = logging.getLogger(__name__)
class SoxPeakTask(task.Task):
description = 'Calculating peak level'
@@ -44,6 +45,7 @@ class SoxPeakTask(task.Task):
self.peak = sox.peak_level(self.track_path)
self.stop()
class FlacEncodeTask(task.Task):
description = 'Encoding to FLAC'
@@ -61,11 +63,13 @@ class FlacEncodeTask(task.Task):
self.new_path = flac.encode(self.track_path, self.track_out_path)
self.stop()
# TODO: Wizzup: Do we really want this as 'Task'...?
# I only made it a task for now because that it's easier to integrate in
# program/cdparanoia.py - where whipper currently does the tagging.
# We should just move the tagging to a more sensible place.
class TaggingTask(task.Task):
# TODO: Wizzup: Do we really want this as 'Task'...?
# I only made it a task for now because that it's easier to integrate in
# program/cdparanoia.py - where whipper currently does the tagging.
# We should just move the tagging to a more sensible place.
description = 'Writing tags to FLAC'
def __init__(self, track_path, tags):

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -12,13 +12,12 @@ class AccurateRipResponseTestCase(tcommon.TestCase):
def testResponse(self):
path = os.path.join(os.path.dirname(__file__),
'dBAR-011-0010e284-009228a3-9809ff0b.bin')
'dBAR-011-0010e284-009228a3-9809ff0b.bin')
data = open(path, "rb").read()
responses = accurip.getAccurateRipResponses(data)
self.assertEquals(len(responses), 3)
response = responses[0]
self.assertEquals(response.trackCount, 11)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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