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