#! /usr/bin/python3 import os import threading import subprocess import time import re import fcntl import traceback import json import logging import dvd import cd CDROM_DRIVE_STATUS = 0x5326 CDS_NO_INFO = 0 CDS_NO_DISC = 1 CDS_TRAY_OPEN = 2 CDS_DRIVE_NOT_READ = 3 CDS_DISC_OK = 4 CDS_STR = ["no info", "no disc", "tray open", "drive not read", "disc ok"] CDROM_DISC_STATUS = 0x5327 CDS_AUDIO = 100 CDS_DATA_1 = 101 CDS_DATA_2 = 102 CDROM_LOCKDOOR = 0x5329 CDROM_EJECT = 0x5309 class Reader(threading.Thread): def __init__(self, device, directory=None, **kwargs): self.device = device self.directory = directory self.status = { "type": "reader", "state": "idle", "device": self.device, } self.complete = 0 self.staleness = 0 self.drive = None logging.info("Starting reader on %s" % self.device) return super().__init__(**kwargs) def reopen(self): if (self.staleness > 15) or not self.drive: if self.drive: self.drive.close() 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: self.staleness += 1 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_audio() elif rv in [CDS_DATA_1, CDS_DATA_2]: self.handle_data() 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): time.sleep(3) else: logging.info("CDROM_DRIVE_STATUS: %d (%s)" % (rv, CDS_STR[rv])) time.sleep(3) def eject(self): self.status["state"] = "ejecting" for i in range(20): try: fcntl.ioctl(self.drive, CDROM_LOCKDOOR, 0) fcntl.ioctl(self.drive, CDROM_EJECT) return except Exception as e: logging.error("Ejecting: %v" % e) time.sleep(i * 5) # XXX: rename this to something like "write_status" def finished(self, **kwargs): self.status["state"] = "finished read" fn = os.path.join(self.directory, self.status["title"], "sucker.json") newfn = fn + ".new" with open(newfn, "w") as fout: json.dump(obj=self.status, fp=fout) os.rename(src=newfn, dst=fn) def handle_audio(self): self.status["video"] = False self.status["state"] = "reading" cd.read(self.device, self.status) directory = os.path.join(self.directory, status["title"]) os.makedirs(directory, exist_ok=True) self.status["state"] = "copying" cd.copy(self.device, self.status, self.directory) self.finished() # XXX: rename this to something like "write_status" def handle_data(self): self.status["video"] = True src = dvd.Copier(self.device, self.status) src.copy(self.directory) self.finished() # vi: sw=4 ts=4 et ai