From c15681a51c9079c4dcd78da5bb9182e673f79000 Mon Sep 17 00:00:00 2001 From: Neale Pickett Date: Fri, 2 Sep 2022 17:04:07 -0600 Subject: [PATCH] Better error handling --- src/dvd.py | 3 +- src/encoder.py | 24 ++++++---------- src/reader.py | 78 ++++++++++++++++++++++++-------------------------- src/sucker.py | 6 ++++ src/worker.py | 37 +++++++++++++++++++----- 5 files changed, 83 insertions(+), 65 deletions(-) diff --git a/src/dvd.py b/src/dvd.py index 365cc77..d409c90 100644 --- a/src/dvd.py +++ b/src/dvd.py @@ -5,6 +5,7 @@ import time import logging import re import os +import shutil SECOND = 1 MINUTE = 60 * SECOND @@ -154,7 +155,7 @@ def encode(state, directory): def clean(state, directory): - os.removedirs(directory) + shutil.rmtree(directory) if __name__ == "__main__": import pprint diff --git a/src/encoder.py b/src/encoder.py index 5b6f7ab..01acbb8 100644 --- a/src/encoder.py +++ b/src/encoder.py @@ -12,30 +12,22 @@ import re import logging import dvd import cd -import traceback import worker class Encoder(worker.Worker): def __init__(self, directory=None): self.status = {} self.directory = directory + self.loop_delay = 12 return super().__init__(directory) - def run(self): - while True: - wait = True - self.status = {"type": "encoder", "state": "idle"} - for fn in glob.glob(self.workdir("*", "sucker.json")): - directory = os.path.dirname(fn) - state = self.read_state(directory) - try: - self.encode(directory, state) - except Exception as e: - logging.error("Error encoding %s: %s" % (directory, e)) - logging.error(traceback.format_exc()) - wait = False - if wait: - time.sleep(12) + def once(self): + wait = True + self.status["type"] = "encoder" + for fn in glob.glob(self.workdir("*", "sucker.json")): + directory = os.path.dirname(fn) + state = self.read_state(directory) + self.encode(directory, state) def encode(self, directory, state): self.status["state"] = "encoding" diff --git a/src/reader.py b/src/reader.py index 28cdedf..3502330 100644 --- a/src/reader.py +++ b/src/reader.py @@ -5,7 +5,6 @@ import subprocess import time import re import fcntl -import traceback import json import logging import slugify @@ -29,58 +28,55 @@ CDS_DATA_2 = 102 CDROM_LOCKDOOR = 0x5329 CDROM_EJECT = 0x5309 +SECOND = 1 +MINUTE = 60 * SECOND +HOUR = 60 * MINUTE + class Reader(worker.Worker): def __init__(self, device, directory): super().__init__(directory) self.device = device - self.status["type"] = "reader" - self.status["device"] = device - self.complete = 0 self.staleness = 0 self.drive = None logging.info("Starting reader on %s" % self.device) def reopen(self): - if (self.staleness > 15) or not self.drive: - if self.drive: - os.close(self.drive) - self.drive = None - try: - self.drive = os.open(self.device, os.O_RDONLY | os.O_NONBLOCK) - logging.debug("Reopened %s" % self.device) - except FileNotFoundError: - pass - self.staleness = 0 - else: + if self.drive and (self.staleness < 15): self.staleness += 1 + return True + self.staleness = 0 + if self.drive: + os.close(self.drive) + self.drive = None + try: + self.drive = os.open(self.device, os.O_RDONLY | os.O_NONBLOCK) + logging.debug("Reopened %s" % self.device) + return True + except FileNotFoundError: + return False - def run(self): - while True: - self.status["state"] = "idle" - self.reopen() - if not self.drive: - time.sleep(30) - continue - rv = fcntl.ioctl(self.drive, CDROM_DRIVE_STATUS) - if rv == CDS_DISC_OK: - rv = fcntl.ioctl(self.drive, CDROM_DISC_STATUS) - try: - if rv == CDS_AUDIO: - self.handle(False) - elif rv in [CDS_DATA_1, CDS_DATA_2]: - self.handle(True) - else: - logging.info("Can't handle disc type %d" % rv) - except Exception as e: - logging.error("Error in disc handler: %s" % e) - logging.error(traceback.format_exc()) - self.eject() - elif rv in (CDS_TRAY_OPEN, CDS_NO_DISC, CDS_DRIVE_NOT_READ): - time.sleep(3) + def once(self): + self.status["type"] = "reader" + self.status["device"] = self.device + if not self.reopen(): + time.sleep(10 * SECOND) + return + rv = fcntl.ioctl(self.drive, CDROM_DRIVE_STATUS) + if rv == CDS_DISC_OK: + rv = fcntl.ioctl(self.drive, CDROM_DISC_STATUS) + if rv == CDS_AUDIO: + self.handle(False) + elif rv in [CDS_DATA_1, CDS_DATA_2]: + self.handle(True) else: - logging.info("CDROM_DRIVE_STATUS: %d (%s)" % (rv, CDS_STR[rv])) - time.sleep(3) - + logging.info("Can't handle disc type %d" % rv) + self.eject() + elif rv in (CDS_TRAY_OPEN, CDS_NO_DISC, CDS_DRIVE_NOT_READ): + time.sleep(3 * SECOND) + else: + logging.info("CDROM_DRIVE_STATUS: %d (%s)" % (rv, CDS_STR[rv])) + time.sleep(3 * SECOND) + def eject(self): for i in range(20): try: diff --git a/src/sucker.py b/src/sucker.py index ab01fb1..2a1db9b 100644 --- a/src/sucker.py +++ b/src/sucker.py @@ -10,14 +10,20 @@ import reader, encoder, statuser class HTTPRequestHandler(http.server.SimpleHTTPRequestHandler): def __init__(self, r, a, s, directory, statuser): self.statuser = statuser + self.quiet = False super().__init__(r, a, s, directory=directory) + def log_request(self, code="-", size="-"): + if not self.quiet: + super().log_request(code, size) + def do_GET(self): if self.path == "/status.json": return self.get_status() return super().do_GET() def get_status(self): + self.quiet = True self.send_response(200) self.end_headers() buf = self.statuser.json().encode("utf-8") diff --git a/src/worker.py b/src/worker.py index e6c8853..75973f5 100644 --- a/src/worker.py +++ b/src/worker.py @@ -2,17 +2,34 @@ import threading import os import json import logging +import time +import traceback + +SECOND = 1 +MINUTE = 60 * SECOND +HOUR = 60 * MINUTE class Worker(threading.Thread): def __init__(self, directory, **kwargs): self.directory = directory - self.status = { - "state": "idle", - } - + self.loop_delay = 2 * SECOND + self.status = {} + kwargs["daemon"] = True return super().__init__(**kwargs) + def run(self): + while True: + self.status = {"state": "idle"} + try: + self.once() + except Exception as exc: + self.status["state"] = "error" + for line in traceback.format_exception(exc): + logging.error(line) + time.sleep(30 * SECOND) + time.sleep(self.loop_delay) + def workdir(self, *path): return os.path.join(self.directory, *path) @@ -25,8 +42,14 @@ class Worker(threading.Thread): os.rename(newstatefn, statefn) def read_state(self, subdir): - with open(self.workdir(subdir, "sucker.json")) as f: - return json.load(f) + try: + with open(self.workdir(subdir, "sucker.json")) as f: + return json.load(f) + except FileNotFoundError: + return {} def clear_state(self, subdir): - os.unlink(self.workdir(subdir, "sucker.json")) + try: + os.unlink(self.workdir(subdir, "sucker.json")) + except FileNotFoundError: + pass