Skip to content

Commit

Permalink
asynchronously be able to parse cdrdao output now
Browse files Browse the repository at this point in the history
Add regexes to cdrdao.py
  • Loading branch information
jtl999 authored and JoeLametta committed Dec 13, 2018
1 parent f02e443 commit a6d33df
Showing 1 changed file with 53 additions and 66 deletions.
119 changes: 53 additions & 66 deletions whipper/program/cdrdao.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import re
import shutil
import tempfile
import subprocess
import time
from subprocess import Popen, PIPE

from whipper.common.common import truncate_filename
Expand All @@ -15,6 +17,19 @@

CDRDAO = 'cdrdao'

_TRACK_RE = re.compile(r"^Analyzing track (?P<track>[0-9]*) \(AUDIO\): start (?P<start>[0-9]*:[0-9]*:[0-9]*), length (?P<length>[0-9]*:[0-9]*:[0-9]*)") # noqa: E501
_CRC_RE = re.compile(
r"Found (?P<channels>[0-9]*) Q sub-channels with CRC errors")
_BEGIN_CDRDAO_RE = re.compile(r"-"*60)
_LAST_TRACK_RE = re.compile(r"^(?P<track>[0-9]*)")
_LEADOUT_RE = re.compile(
r"^Leadout AUDIO\s*[0-9]\s*[0-9]*:[0-9]*:[0-9]*\([0-9]*\)")

class ProgressParser:
def parse(self, line):
pass


class ReadTOC_Task(task.Task):
"""
Task that reads the TOC of the disc using cdrdao
Expand All @@ -25,18 +40,20 @@ class ReadTOC_Task(task.Task):
def __init__(self, device, fast_toc=False, toc_path=None):
"""
Read the TOC for 'device'.
@device: path of device
@type device: str
@param fast_toc: use cdrdao fast-toc mode
@type fast_toc: bool
@param toc_path: Where to save the generated table of contents
@type str
@param device: block device to read TOC from
@type device: str
@param fast_toc: If to use fast-toc cdrdao mode
@type fast_toc: bool
@param toc_path: Where to save TOC if wanted.
@type toc_path: str
"""

self.device = device
self.fast_toc = fast_toc
self.toc_path = toc_path

self._buffer = "" # accumulate characters

def start(self, runner):
task.Task.start(self, runner)
os.close(self.fd)
Expand Down Expand Up @@ -80,10 +97,39 @@ def _read(self, runner):

# 0 does not give us output before we complete, 1.0 gives us output
# too late

self._start_time = time.time()
self.schedule(1.0, self._read, runner)

def _read(self, runner):
ret = self._popen.recv_err()
if not ret:
if self._popen.poll() is not None:
self._done()
return
self.schedule(0.01, self._read, runner)
return
self._buffer += ret

# parse buffer into lines if possible, and parse them
if "\n" in self._buffer:

lines = self._buffer.split('\n')
if lines[-1] != "\n":
# last line didn't end yet
self._buffer = lines[-1]
del lines[-1]
else:
self._buffer = ""
for line in lines:
sys.stdout.write("%s\n" % line)

# 0 does not give us output before we complete, 1.0 gives us output
# too late
self.schedule(0.01, self._read, runner)

def _poll(self, runner):

if self._popen.poll() is None:
self.schedule(1.0, self._poll, runner)
return
Expand All @@ -108,49 +154,6 @@ def _done(self):
self.stop()
return

def read_toc(device, fast_toc=False, toc_path=None):
"""
Return cdrdao-generated table of contents for 'device'.
"""
# cdrdao MUST be passed a non-existing filename as its last argument
# to write the TOC to; it does not support writing to stdout or
# overwriting an existing file, nor does linux seem to support
# locking a non-existant file. Thus, this race-condition introducing
# hack is carried from morituri to whipper and will be removed when
# cdrdao is fixed.
fd, tocfile = tempfile.mkstemp(suffix=u'.cdrdao.read-toc.whipper')
os.close(fd)
os.unlink(tocfile)

cmd = [CDRDAO, 'read-toc'] + (['--fast-toc'] if fast_toc else []) + [
'--device', device, tocfile]
# PIPE is the closest to >/dev/null we can get
logger.debug("executing %r", cmd)
p = Popen(cmd, stdout=PIPE, stderr=PIPE)
_, 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()
if toc_path is not None:
t_comp = os.path.abspath(toc_path).split(os.sep)
t_dirn = os.sep.join(t_comp[:-1])
# If the output path doesn't exist, make it recursively
if not os.path.isdir(t_dirn):
os.makedirs(t_dirn)
t_dst = truncate_filename(os.path.join(t_dirn, t_comp[-1] + '.toc'))
shutil.copy(tocfile, os.path.join(t_dirn, t_dst))
os.unlink(tocfile)
return toc


def DetectCdr(device):
"""
Return whether cdrdao detects a CD-R for 'device'.
Expand All @@ -163,7 +166,6 @@ def DetectCdr(device):
else:
return True


def version():
"""
Return cdrdao version as a string.
Expand All @@ -182,21 +184,6 @@ def version():
return None
return m.group('version')


def ReadTOCTask(device):
"""
stopgap morituri-insanity compatibility layer
"""
return read_toc(device, fast_toc=True)


def ReadTableTask(device, toc_path=None):
"""
stopgap morituri-insanity compatibility layer
"""
return read_toc(device, toc_path=toc_path)


def getCDRDAOVersion():
"""
stopgap morituri-insanity compatibility layer
Expand Down

0 comments on commit a6d33df

Please sign in to comment.