cdrdao no-disc ejection & --eject (#93)

* cdrdao: eject empty disc while reading toc

- introduce EjectError
- move {eject,load,unmount}_device to program.utils

* remove duplicated eject

* remove unnecessary ejection

* add --eject option
This commit is contained in:
Samantha Baldwin
2017-01-01 13:41:23 -05:00
committed by JoeLametta
parent 17021a68f2
commit a1eb3377ea
7 changed files with 83 additions and 47 deletions

View File

@@ -34,7 +34,7 @@ from morituri.command.basecommand import BaseCommand
from morituri.common import (
accurip, common, config, drive, gstreamer, program, task
)
from morituri.program import cdrdao, cdparanoia
from morituri.program import cdrdao, cdparanoia, utils
from morituri.result import result
import logging
@@ -108,8 +108,8 @@ class _CD(BaseCommand):
self.device = self.options.device
sys.stdout.write('Checking device %s\n' % self.device)
self.program.loadDevice(self.device)
self.program.unmountDevice(self.device)
utils.load_device(self.device)
utils.unmount_device(self.device)
# first, read the normal TOC, which is fast
self.ittoc = self.program.getFastToc(self.runner,
@@ -140,8 +140,8 @@ class _CD(BaseCommand):
# also used by rip cd info
if not getattr(self.options, 'unknown', False):
if self.eject:
self.program.ejectDevice(self.device)
logger.critical("unable to retrieve disc metadata, "
"--unknown not passed")
return -1
# FIXME ?????
@@ -205,8 +205,8 @@ class _CD(BaseCommand):
self.doCommand()
if self.eject:
self.program.ejectDevice(self.device)
if self.options.eject in ('success', 'always'):
utils.eject_device(self.device)
def doCommand(self):
pass
@@ -587,8 +587,6 @@ Log files will log the path to tracks relative to this directory.
# write log file
self.program.writeLog(discName, self.logger)
self.program.ejectDevice(self.device)
class CD(BaseCommand):
summary = "handle CDs"

View File

@@ -12,6 +12,7 @@ from morituri.command import cd, offset, drive, image, accurip, debug
from morituri.command.basecommand import BaseCommand
from morituri.common import common, directory
from morituri.extern.task import task
from morituri.program.utils import eject_device
import logging
logger = logging.getLogger(__name__)
@@ -26,9 +27,13 @@ def main():
)
map(pkg_resources.working_set.add, distributions)
try:
ret = Whipper(sys.argv[1:], os.path.basename(sys.argv[0]), None).do()
cmd = Whipper(sys.argv[1:], os.path.basename(sys.argv[0]), None)
ret = cmd.do()
except SystemError, e:
sys.stderr.write('whipper: error: %s\n' % e.args)
sys.stderr.write('whipper: error: %s\n' % e)
if (type(e) is common.EjectError and
cmd.options.eject in ('failure', 'always')):
eject_device(e.device)
return 255
except ImportError, e:
raise ImportError(e)
@@ -77,6 +82,10 @@ You can get help on subcommands by using the -h option to the subcommand.
self.parser.add_argument('-h', '--help',
action="store_true", dest="help",
help="show this help message and exit")
self.parser.add_argument('-e', '--eject',
action="store", dest="eject", default="always",
choices=('never', 'failure', 'success', 'always'),
help="when to eject disc (default: always)")
def handle_arguments(self):
if self.options.help:

View File

@@ -31,7 +31,7 @@ gobject.threads_init()
from morituri.command.basecommand import BaseCommand
from morituri.common import accurip, common, config, drive, program
from morituri.common import task as ctask
from morituri.program import cdrdao, cdparanoia
from morituri.program import cdrdao, cdparanoia, utils
from morituri.extern.task import task
@@ -88,8 +88,8 @@ CD in the AccurateRip database."""
# if necessary, load and unmount
sys.stdout.write('Checking device %s\n' % device)
prog.loadDevice(device)
prog.unmountDevice(device)
utils.load_device(device)
utils.unmount_device(device)
# first get the Table Of Contents of the CD
t = cdrdao.ReadTOCTask(device)

View File

@@ -39,6 +39,19 @@ WORDS_PER_FRAME = SAMPLES_PER_FRAME * 2
BYTES_PER_FRAME = SAMPLES_PER_FRAME * 4
class EjectError(SystemError):
"""
Possibly ejects the drive in command.main.
"""
def __init__(self, device, *args):
"""
args is a tuple used by BaseException.__str__
device is the device path to eject
"""
self.args = args
self.device = device
def msfToFrames(msf):
"""
Converts a string value in MM:SS:FF to frames.

View File

@@ -90,32 +90,6 @@ class Program:
logger.info('Changing to working directory %s' % workingDirectory)
os.chdir(workingDirectory)
def loadDevice(self, device):
"""
Load the given device.
"""
os.system('eject -t %s' % device)
def ejectDevice(self, device):
"""
Eject the given device.
"""
os.system('eject %s' % device)
def unmountDevice(self, device):
"""
Unmount the given device if it is mounted, as happens with automounted
data tracks.
If the given device is a symlink, the target will be checked.
"""
device = os.path.realpath(device)
logger.debug('possibly unmount real path %r' % device)
proc = open('/proc/mounts').read()
if device in proc:
print 'Device %s is mounted, unmounting' % device
os.system('umount %s' % device)
def getFastToc(self, runner, toc_pickle, device):
"""
Retrieve the normal TOC table from a toc pickle or the drive.

View File

@@ -1,8 +1,9 @@
import os
import re
import tempfile
from subprocess import check_call, Popen, PIPE, CalledProcessError
from subprocess import Popen, PIPE
from morituri.common.common import EjectError
from morituri.image.toc import TocFile
import logging
@@ -27,12 +28,18 @@ def read_toc(device, fast_toc=False):
cmd = [CDRDAO, 'read-toc'] + (['--fast-toc'] if fast_toc else []) + [
'--device', device, tocfile]
# PIPE is the closest to >/dev/null we can get
try:
check_call(cmd, stdout=PIPE, stderr=PIPE)
except CalledProcessError, e:
logger.warning('cdrdao read-toc failed: return code is non-zero: ' +
str(e.returncode))
raise e
logger.debug("executing %r", cmd)
p = Popen(cmd, stdout=PIPE, stderr=PIPE)
_, stderr = p.communicate()
if p.returncode != 0:
msg = 'cdrdao read-toc failed: return code is non-zero: ' + \
str(p.returncode)
logger.critical(msg)
# Gracefully handle missing disc
if "ERROR: Unit not ready, giving up." in stderr:
raise EjectError(device, "no disc detected")
raise IOError(msg)
toc = TocFile(tocfile)
toc.parse()
os.unlink(tocfile)

35
morituri/program/utils.py Normal file
View File

@@ -0,0 +1,35 @@
import os
import logging
logger = logging.getLogger(__name__)
def eject_device(device):
"""
Eject the given device.
"""
logger.debug("ejecting device %s", device)
os.system('eject %s' % device)
def load_device(device):
"""
Load the given device.
"""
logger.debug("loading (eject -t) device %s", device)
os.system('eject -t %s' % device)
def unmount_device(device):
"""
Unmount the given device if it is mounted, as happens with automounted
data tracks.
If the given device is a symlink, the target will be checked.
"""
device = os.path.realpath(device)
logger.debug('possibly unmount real path %r' % device)
proc = open('/proc/mounts').read()
if device in proc:
print 'Device %s is mounted, unmounting' % device
os.system('umount %s' % device)